comparison orpg/networking/mplay_client.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 d5e81dac98ff
comparison
equal deleted inserted replaced
-1:000000000000 0:4385a7d0efd1
1 # Copyright (C) 2000-2001 The OpenRPG Project
2 #
3 # openrpg-dev@lists.sourceforge.net
4 #
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 2 of the License, or
8 # (at your option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 # --
19 #
20 # File: mplay_client.py
21 # Author: Chris Davis
22 # Maintainer:
23 # Version:
24 # $Id: mplay_client.py,v 1.71 2007/05/12 20:41:54 digitalxero Exp $
25 #
26 # Description: This file contains the code for the client stubs of the multiplayer
27 # features in the orpg project.
28 #
29
30 __version__ = "$Id: mplay_client.py,v 1.71 2007/05/12 20:41:54 digitalxero Exp $"
31
32 import orpg.minidom
33 import socket
34 import Queue
35 import thread
36 import traceback
37 from threading import Event, Lock
38 from xml.sax.saxutils import escape
39 from struct import pack, unpack, calcsize
40 from string import *
41 from orpg.orpg_version import *
42 import errno
43 import os
44 import time
45
46 try:
47 import bz2
48 cmpBZ2 = True
49 except:
50 cmpBZ2 = False
51
52 try:
53 import zlib
54 cmpZLIB = True
55 except:
56 cmpZLIB = False
57
58
59 # This should be configurable
60 OPENRPG_PORT = 9557
61
62 # We should be sending a length for each packet
63 MPLAY_LENSIZE = calcsize( 'i' )
64 MPLAY_DISCONNECTED = 0
65 MPLAY_CONNECTED = 1
66 MPLAY_DISCONNECTING = 3
67 MPLAY_GROUP_CHANGE = 4
68 MPLAY_GROUP_CHANGE_F = 5
69 PLAYER_NEW = 1
70 PLAYER_DEL = 2
71 PLAYER_GROUP = 3
72
73 # The next two messages are used to inform others that a player is typing
74 PLAYER_TYPING = 4
75 PLAYER_NOT_TYPING = 5
76 PLAYER_UPDATE = 6
77 GROUP_JOIN = 1
78 GROUP_NEW = 2
79 GROUP_DEL = 3
80 GROUP_UPDATE = 4
81 STATUS_SET_URL = 1
82
83 def parseXml(data):
84 "parse and return doc"
85 #print data
86 doc = orpg.minidom.parseString(data)
87 doc.normalize()
88 return doc
89
90 def myescape(data):
91 return escape(data,{"\"":""})
92
93 class mplay_event:
94 def __init__(self,id,data=None):
95 self.id = id
96 self.data = data
97
98 def get_id(self):
99 return self.id
100
101 def get_data(self):
102 return self.data
103
104 BOOT_MSG = "YoU ArE ThE WeAkEsT LiNk. GoOdByE."
105
106 class client_base:
107
108 # Player role definitions
109 def __init__(self):
110 self.outbox = Queue.Queue(0)
111 self.inbox = Queue.Queue(0)
112 self.startedEvent = Event()
113 self.exitEvent = Event()
114 self.sendThreadExitEvent = Event()
115 self.recvThreadExitEvent = Event()
116 self.id = "0"
117 self.group_id = "0"
118 self.name = ""
119 self.role = "GM"
120 self.ROLE_GM = "GM"
121 self.ROLE_PLAYER = "PLAYER"
122 self.ROLE_LURKER = "LURKER"
123 self.ip = socket.gethostbyname(socket.gethostname())
124 self.remote_ip = None
125 self.version = VERSION
126 self.protocol_version = PROTOCOL_VERSION
127 self.client_string = CLIENT_STRING
128 self.status = MPLAY_DISCONNECTED
129 self.useCompression = False
130 self.compressionType = 'Undefined'
131 self.log_console = None
132 self.sock = None
133 self.text_status = "Idle"
134 self.statLock = Lock()
135 self.useroles = 0
136 self.ROLE_GM = "GM"
137 self.ROLE_PLAYER = "PLAYER"
138 self.ROLE_LURKER = "LURKER"
139 self.lastmessagetime = time.time()
140 self.connecttime = time.time()
141
142 def sendThread( self, arg ):
143 "Sending thread. This thread reads from the data queue and writes to the socket."
144
145 # Wait to be told it's okay to start running
146 self.startedEvent.wait()
147
148 # Loop as long as we have a connection
149 while( self.get_status() == MPLAY_CONNECTED ):
150 try:
151 readMsg = self.outbox.get( block=1 )
152 except Exception, text:
153 self.log_msg( ("outbox.get() got an exception: ", text) )
154
155 # If we are here, it's because we have data to send, no doubt!
156 if self.status == MPLAY_CONNECTED:
157 try:
158 # Send the entire message, properly formated/encoded
159 sent = self.sendMsg( self.sock, readMsg )
160 except Exception, e:
161 self.log_msg( e )
162 else:
163 # If we are not connected, purge the data queue
164 self.log_msg( "Data queued without a connection, purging data from queue..." )
165 self.sendThreadExitEvent.set()
166 self.log_msg( "sendThread has terminated..." )
167
168 def recvThread( self, arg ):
169 "Receiving thread. This thread reads from the socket and writes to the data queue."
170
171 # Wait to be told it's okay to start running
172 self.startedEvent.wait()
173
174 while( self.get_status() == MPLAY_CONNECTED ):
175 readMsg = self.recvMsg( self.sock )
176 try:
177 if self.useCompression and self.compressionType != None:
178 readMsg = self.compressionType.decompress(readMsg)
179 except:
180 pass
181
182 # Check the length of the message
183 bytes = len( readMsg )
184
185 # Make sure we are still connected
186 if bytes == 0:
187 break
188 else:
189 # Pass along the message so it can be processed
190 self.inbox.put( readMsg )
191 self.update_idle_time() #update the last message time
192 if bytes == 0:
193 self.log_msg( "Remote has disconnected!" )
194 self.set_status( MPLAY_DISCONNECTING )
195 self.outbox.put( "" ) # Make sure the other thread is woken up!
196 self.sendThreadExitEvent.set()
197 self.log_msg( "recvThread has terminated..." )
198
199 def sendMsg( self, sock, msg ):
200 """Very simple function that will properly encode and send a message to te
201 remote on the specified socket."""
202
203 if self.useCompression and self.compressionType != None:
204 mpacket = self.compressionType.compress(msg)
205 lpacket = pack('!i', len(mpacket))
206 sock.send(lpacket)
207 offset = 0
208 while offset < len(mpacket):
209 slice = buffer(mpacket, offset, len(mpacket)-offset)
210 sent = sock.send(slice)
211 offset += sent
212 sentm = offset
213 else:
214 # Calculate our message length
215 length = len(msg)
216
217 # Encode the message length into network byte order
218 lp = pack('!i', length)
219
220 try:
221 # Send the encoded length
222 sentl = sock.send( lp )
223
224 # Now, send the message the the length was describing
225 sentm = sock.send( msg )
226 if self.isServer():
227 self.log_msg(("data_sent", sentl+sentm))
228 except socket.error, e:
229 self.log_msg( e )
230 except Exception, e:
231 self.log_msg( e )
232 return sentm
233
234 def recvData( self, sock, readSize ):
235 """Simple socket receive method. This method will only return when the exact
236 byte count has been read from the connection, if remote terminates our
237 connection or we get some other socket exception."""
238 data = ""
239 offset = 0
240 try:
241 while offset != readSize:
242 frag = sock.recv( readSize - offset )
243 # See if we've been disconnected
244 rs = len( frag )
245 if rs <= 0:
246 # Loudly raise an exception because we've been disconnected!
247 raise IOError, "Remote closed the connection!"
248 else:
249 # Continue to build complete message
250 offset += rs
251 data += frag
252 except socket.error, e:
253 self.log_msg( e )
254 data = ""
255 return data
256
257 def recvMsg( self, sock ):
258 """This method now expects to receive a message having a 4-byte prefix length. It will ONLY read
259 completed messages. In the event that the remote's connection is terminated, it will throw an
260 exception which should allow for the caller to more gracefully handle this exception event.
261
262 Because we use strictly reading ONLY based on the length that is told to use, we no longer have to
263 worry about partially adjusting for fragmented buffers starting somewhere within a buffer that we've
264 read. Rather, it will get ONLY a whole message and nothing more. Everything else will remain buffered
265 with the OS until we attempt to read the next complete message."""
266
267 msgData = ""
268 try:
269 lenData = self.recvData( sock, MPLAY_LENSIZE )
270 # Now, convert to a usable form
271 (length,) = unpack('!i', lenData)
272 # Read exactly the remaining amount of data
273 msgData = self.recvData( sock, length )
274 if self.isServer():
275 self.log_msg(("data_recv", length+4))
276 # Make the peer IP address available for reference later
277 if self.remote_ip is None:
278 self.remote_ip = self.sock.getpeername()
279 except IOError, e:
280 self.log_msg( e )
281 except Exception, e:
282 self.log_msg( e )
283 return msgData
284
285 def initialize_threads(self):
286 "Starts up our threads (2) and waits for them to make sure they are running!"
287 self.status = MPLAY_CONNECTED
288 self.sock.setblocking(1)
289 # Confirm that our threads have started
290 thread.start_new_thread( self.sendThread,(0,) )
291 thread.start_new_thread( self.recvThread,(0,) )
292 self.startedEvent.set()
293
294 def disconnect(self):
295 self.set_status(MPLAY_DISCONNECTING)
296 self.log_msg("client stub " + self.ip +" disconnecting...")
297 self.log_msg("closing sockets...")
298 try:
299 self.sock.shutdown( 2 )
300 except Exception, e:
301 print "Caught exception: " + str(e)
302 print
303 print "Continuing"
304 self.set_status(MPLAY_DISCONNECTED)
305
306 def reset(self,sock):
307 self.disconnect()
308 self.sock = sock
309 self.initialize_threads()
310
311 def update_role(self,role):
312 self.useroles = 1
313 self.role = role
314
315 def use_roles(self):
316 if self.useroles:
317 return 1
318 else:
319 return 0
320 def update_self_from_player(self, player):
321 try:
322 (self.name, self.ip, self.id, self.text_status, self.version, self.protocol_version, self.client_string,role) = player
323 except Exception, e:
324 print e
325
326 # The IP field should really be deprecated as too many systems are NAT'd and/or behind firewalls for a
327 # client provided IP address to have much value. As such, we now label it as deprecated.
328 def toxml(self,action):
329 xml_data = '<player name="' + myescape(self.name) + '"'
330 xml_data += ' action="' + action + '" id="' + self.id + '"'
331 xml_data += ' group_id="' + self.group_id + '" ip="' + self.ip + '"'
332 xml_data += ' status="' + self.text_status + '"'
333 xml_data += ' version="' + self.version + '"'
334 xml_data += ' protocol_version="' + self.protocol_version + '"'
335 xml_data += ' client_string="' + self.client_string + '"'
336 xml_data += ' useCompression="' + str(self.useCompression) + '"'
337 if cmpBZ2 and (self.compressionType == 'Undefined' or self.compressionType == bz2):
338 xml_data += ' cmpType="bz2"'
339 elif cmpZLIB and (self.compressionType == 'Undefined' or self.compressionType == zlib):
340 xml_data += ' cmpType="zlib"'
341 else:
342 xml_data += ' cmpType="None"'
343 xml_data += ' />'
344 return xml_data
345
346 def log_msg(self,msg):
347 if self.log_console:
348 self.log_console(msg)
349 # else:
350 # print "message", msg
351
352 def get_status(self):
353 self.statLock.acquire()
354 status = self.status
355 self.statLock.release()
356 return status
357
358 def my_role(self):
359 #Why create the three different objects? Why not just assign a value to self.role and use that? Prof_Ebral ponders.
360 if self.role == "GM":
361 return self.ROLE_GM
362 elif self.role == "Player":
363 return self.ROLE_PLAYER
364 elif self.role == "Lurker":
365 return self.ROLE_LURKER
366 return -1
367
368 def set_status(self,status):
369 self.statLock.acquire()
370 self.status = status
371 self.statLock.release()
372
373 def isServer( self ):
374 # Return 1 if we are running as a server, else, return 0.
375 # This method must be overloaded by whomever derives from us
376 pass
377
378 def __str__(self):
379 return "%s(%s)\nIP:%s\ngroup_id:%s\n" % (self.name, self.id, self.ip, self.group_id)
380
381 # idle time functions added by snowdog 3/31/04
382 def update_idle_time(self):
383 self.lastmessagetime = time.time()
384
385 def idle_time(self):
386 curtime = time.time()
387 idletime = curtime - self.lastmessagetime
388 return idletime
389
390 def idle_status(self):
391 idletime = self.idle_time()
392 idlemins = idletime / 60
393 status = "Unknown"
394 if idlemins < 3:
395 status = "Active"
396 elif idlemins < 10:
397 status = "Idle ("+str(int(idlemins))+" mins)"
398 else:
399 status = "Inactive ("+str(int(idlemins))+" mins)"
400 return status
401
402 def connected_time(self):
403 curtime = time.time()
404 timeoffset = curtime - self.connecttime
405 return timeoffset
406
407 def connected_time_string(self):
408 "returns the time client has been connected as a formated time string"
409 ct = self.connected_time()
410 d = int(ct/86400)
411 h = int( (ct-(86400*d))/3600 )
412 m = int( (ct-(86400*d)-(3600*h))/60)
413 s = int( (ct-(86400*d)-(3600*h)-(60*m)) )
414 cts = zfill(d,2)+":"+zfill(h,2)+":"+zfill(m,2)+":"+zfill(s,2)
415 return cts
416
417 #========================================================================
418 #
419 #
420 # MPLAY CLIENT
421 #
422 #
423 #========================================================================
424 class mplay_client(client_base):
425 "mplay client"
426 def __init__(self,name,callbacks):
427 client_base.__init__(self)
428 self.set_name(name)
429 self.on_receive = callbacks['on_receive']
430 self.on_mplay_event = callbacks['on_mplay_event']
431 self.on_group_event = callbacks['on_group_event']
432 self.on_player_event = callbacks['on_player_event']
433 self.on_status_event = callbacks['on_status_event']
434 self.on_password_signal = callbacks['on_password_signal']
435 # I know this is a bad thing to do but it has to be
436 # be done to use the unified password manager.
437 # Should really find a better solution. -- SD 8/03
438 self.orpgFrame_callback = callbacks['orpgFrame']
439 self.settings = self.orpgFrame_callback.settings
440 #self.version = VERSION
441 #self.protocol_version = PROTOCOL_VERSION
442 #self.client_string = CLIENT_STRING
443 self.ignore_id = []
444 self.ignore_name = []
445 self.players = {}
446 self.groups = {}
447 self.unique_cookie = 0
448 self.msg_handlers = {}
449 self.core_msg_handlers = []
450 self.load_core_msg_handlers()
451
452 # implement from our base class
453 def isServer(self):
454 return 0
455
456 def get_chat(self):
457 return self.orpgFrame_callback.chat
458
459 def set_name(self,name):
460 self.name = name
461 self.update()
462
463 def set_text_status(self, status):
464 if self.text_status != status:
465 self.text_status = status
466 self.update()
467
468 def set_status_url(self, url="None"):
469 self.on_status_event(mplay_event(STATUS_SET_URL,url))
470
471 def update(self, evt=None):
472 if self.status == MPLAY_CONNECTED:
473 self.outbox.put(self.toxml('update'))
474 self.inbox.put(self.toxml('update'))
475
476 def get_group_info(self, id=0):
477 self.statLock.acquire()
478 id = self.groups[id]
479 self.statLock.release()
480 return id
481
482 def get_my_group(self):
483 self.statLock.acquire()
484 id = self.groups[self.group_id]
485 self.statLock.release()
486 return id
487
488 def get_groups(self):
489 self.statLock.acquire()
490 groups = self.groups.values()
491 self.statLock.release()
492 return groups
493
494 def get_players(self):
495 self.statLock.acquire()
496 players = self.players.values()
497 self.statLock.release()
498 return players
499
500 def get_player_info(self,id):
501 self.statLock.acquire()
502 player = self.players[id]
503 self.statLock.release()
504 return player
505
506 def get_player_by_player_id(self,player):
507 players = self.get_players()
508 if self.players.has_key(player):
509 for m in players:
510 if player == m[2]:
511 return m
512 return -1
513
514 def get_id(self):
515 return self.id
516
517 def get_my_info(self):
518 return (self.name, self.ip, self.id, self.text_status, self.version, self.protocol_version, self.client_string, self.role)
519
520 def is_valid_id(self,id):
521 self.statLock.acquire()
522 value = self.players.has_key( id )
523 self.statLock.release()
524 return value
525
526 def clear_players(self,save_self=0):
527 self.statLock.acquire()
528 keys = self.players.keys()
529 for k in keys:
530 del self.players[k]
531 self.statLock.release()
532
533 def clear_groups(self):
534 self.statLock.acquire()
535 keys = self.groups.keys()
536 for k in keys:
537 del self.groups[k]
538 self.statLock.release()
539
540 def find_role(self,id):
541 return self.players[id].role
542
543 def get_ignore_list(self):
544 try:
545 return (self.ignore_id, self.ignore_name)
546 except:
547 return (None, None)
548
549 def toggle_ignore(self, id):
550 for m in self.ignore_id:
551 if `self.ignore_id[self.ignore_id.index(m)]` == `id`:
552 name = self.ignore_name[self.ignore_id.index(m)]
553 self.ignore_id.remove(m)
554 self.ignore_name.remove(name)
555 return (0,id,name)
556 self.ignore_name.append(self.players[id][0])
557 self.ignore_id.append(self.players[id][2])
558 return (1,self.players[id][2],self.players[id][0])
559
560 def boot_player(self,id,boot_pwd = ""):
561 #self.send(BOOT_MSG,id)
562 msg = '<boot boot_pwd="' + boot_pwd + '"/>'
563 self.send(msg,id)
564
565 #---------------------------------------------------------
566 # [START] Snowdog Password/Room Name altering code 12/02
567 #---------------------------------------------------------
568
569 def set_room_pass(self,npwd,pwd=""):
570 self.outbox.put("<alter key=\"pwd\" val=\"" +npwd+ "\" bpw=\"" + pwd + "\" plr=\"" + self.id +"\" gid=\"" + self.group_id + "\" />")
571 self.update()
572
573 def set_room_name(self,name,pwd=""):
574 loc = name.find("&")
575 oldloc=0
576 while loc > -1:
577 loc = name.find("&",oldloc)
578 if loc > -1:
579 b = name[:loc]
580 e = name[loc+1:]
581 name = b + "&amp;" + e
582 oldloc = loc+1
583 loc = name.find('"')
584 oldloc=0
585 while loc > -1:
586 loc = name.find('"',oldloc)
587 if loc > -1:
588 b = name[:loc]
589 e = name[loc+1:]
590 name = b + "&quot;" + e
591 oldloc = loc+1
592 loc = name.find("'")
593 oldloc=0
594 while loc > -1:
595 loc = name.find("'",oldloc)
596 if loc > -1:
597 b = name[:loc]
598 e = name[loc+1:]
599 name = b + "&#39;" + e
600 oldloc = loc+1
601 self.outbox.put("<alter key=\"name\" val=\"" + name + "\" bpw=\"" + pwd + "\" plr=\"" + self.id +"\" gid=\"" + self.group_id + "\" />")
602 self.update()
603
604 #---------------------------------------------------------
605 # [END] Snowdog Password/Room Name altering code 12/02
606 #---------------------------------------------------------
607
608 def display_roles(self):
609 self.outbox.put("<role action=\"display\" player=\"" + self.id +"\" group_id=\""+self.group_id + "\" />")
610
611 def get_role(self):
612 self.outbox.put("<role action=\"get\" player=\"" + self.id +"\" group_id=\""+self.group_id + "\" />")
613
614 def set_role(self,player,role,pwd=""):
615 self.outbox.put("<role action=\"set\" player=\"" + player + "\" role=\"" +role+ "\" boot_pwd=\"" + pwd + "\" group_id=\"" + self.group_id + "\" />")
616 self.update()
617
618 def send(self,msg,player="all"):
619 if self.status == MPLAY_CONNECTED and player != self.id:
620 self.outbox.put("<msg to='"+player+"' from='"+self.id+"' group_id='"+self.group_id+"' />"+msg)
621 self.check_my_status()
622
623 def send_sound(self, snd_xml):
624 if self.status == MPLAY_CONNECTED:
625 self.outbox.put(snd_xml)
626 self.check_my_status()
627
628 def send_create_group(self,name,pwd,boot_pwd,minversion):
629 self.outbox.put("<create_group from=\""+self.id+"\" pwd=\""+pwd+"\" name=\""+
630 name+"\" boot_pwd=\""+boot_pwd+"\" min_version=\"" + minversion +"\" />")
631
632 def send_join_group(self,group_id,pwd):
633 if (group_id != 0):
634 self.update_role("LURKER")
635 self.outbox.put("<join_group from=\""+self.id+"\" pwd=\""+pwd+"\" group_id=\""+str(group_id)+"\" />")
636
637 def poll(self, evt=None):
638 try:
639 msg = self.inbox.get_nowait()
640 except:
641 if self.get_status() != MPLAY_CONNECTED:
642 self.check_my_status()
643 else:
644 try:
645 self.pretranslate(msg)
646 except Exception, e:
647 print "The following message: " + str(msg)
648 print "created the following exception: "
649 traceback.print_exc()
650
651 def add_msg_handler(self, tag, function, core=False):
652 if not self.msg_handlers.has_key(tag):
653 self.msg_handlers[tag] = function
654 if core:
655 self.core_msg_handlers.append(tag)
656 else:
657 print 'XML Messages ' + tag + ' already has a handler'
658
659 def remove_msg_handler(self, tag):
660 if self.msg_handlers.has_key(tag) and not tag in self.core_msg_handlers:
661 del self.msg_handlers[tag]
662 else:
663 print 'XML Messages ' + tag + ' already deleted'
664
665 def load_core_msg_handlers(self):
666 self.add_msg_handler('msg', self.on_msg, True)
667 self.add_msg_handler('ping', self.on_ping, True)
668 self.add_msg_handler('group', self.on_group, True)
669 self.add_msg_handler('role', self.on_role, True)
670 self.add_msg_handler('player', self.on_player, True)
671 self.add_msg_handler('password', self.on_password, True)
672 self.add_msg_handler('sound', self.on_sound, True)
673
674 def pretranslate(self,data):
675 # Pre-qualify our data. If we don't have atleast 5-bytes, then there is
676 # no way we even have a valid message!
677 if len(data) < 5:
678 return
679 end = data.find(">")
680 head = data[:end+1]
681 msg = data[end+1:]
682 xml_dom = parseXml(head)
683 xml_dom = xml_dom._get_documentElement()
684 tag_name = xml_dom._get_tagName()
685 id = xml_dom.getAttribute("from")
686 if id == '':
687 id = xml_dom.getAttribute("id")
688 if self.msg_handlers.has_key(tag_name):
689 self.msg_handlers[tag_name](id, data, xml_dom)
690 else:
691 #Unknown messages recived ignoring
692 #using pass insted or printing an error message
693 #because plugins should now be able to send and proccess messages
694 #if someone is using a plugin to send messages and this user does not
695 #have the plugin they would be getting errors
696 pass
697 if xml_dom:
698 xml_dom.unlink()
699
700 def on_sound(self, id, data, xml_dom):
701 (ignore_id,ignore_name) = self.get_ignore_list()
702 for m in ignore_id:
703 if m == id:
704 # yes we are
705 print "ignoring sound from player:"
706 return
707 chat = self.get_chat()
708 snd = xml_dom.getAttribute("url")
709 loop_sound = xml_dom.getAttribute("loop")
710 chat.sound_player.play(snd, "remote", loop_sound)
711
712 def on_msg(self, id, data, xml_dom):
713 end = data.find(">")
714 head = data[:end+1]
715 msg = data[end+1:]
716 if id == "0":
717 self.on_receive(msg,None) # None get's interpreted in on_receive as the sys admin.
718 # Doing it this way makes it harder to impersonate the admin
719 else:
720 if self.is_valid_id(id):
721 self.on_receive(msg,self.players[id])
722 if xml_dom:
723 xml_dom.unlink()
724
725 def on_ping(self, id, msg, xml_dom):
726 #a REAL ping time implementation by Snowdog 8/03
727 # recieves special server <ping time="###" /> command
728 # where ### is a returning time from the clients ping command
729 #get current time, pull old time from object and compare them
730 # the difference is the latency between server and client * 2
731 ct = time.clock()
732 ot = xml_dom.getAttribute("time")
733 latency = float(float(ct) - float(ot))
734 latency = int( latency * 10000.0 )
735 latency = float( latency) / 10.0
736 ping_msg = "Ping Results: " + str(latency) + " ms (parsed message, round trip)"
737 self.on_receive(ping_msg,None)
738 if xml_dom:
739 xml_dom.unlink()
740
741 def on_group(self, id, msg, xml_dom):
742 name = xml_dom.getAttribute("name")
743 players = xml_dom.getAttribute("players")
744 act = xml_dom.getAttribute("action")
745 pwd = xml_dom.getAttribute("pwd")
746 group_data = (id, name, pwd, players)
747
748 if act == 'new':
749 self.groups[id] = group_data
750 self.on_group_event(mplay_event(GROUP_NEW, group_data))
751 elif act == 'del':
752 del self.groups[id]
753 self.on_group_event(mplay_event(GROUP_DEL, group_data))
754 elif act == 'update':
755 self.groups[id] = group_data
756 self.on_group_event(mplay_event(GROUP_UPDATE, group_data))
757 if xml_dom:
758 xml_dom.unlink()
759
760 def on_role(self, id, msg, xml_dom):
761 act = xml_dom.getAttribute("action")
762 role = xml_dom.getAttribute("role")
763 if (act == "set") or (act == "update"):
764 try:
765 (a,b,c,d,e,f,g,h) = self.players[id]
766 if id == self.id:
767 self.players[id] = (a,b,c,d,e,f,g,role)
768 self.update_role(role)
769 else:
770 self.players[id] = (a,b,c,d,e,f,g,role)
771 self.on_player_event(mplay_event(PLAYER_UPDATE,self.players[id]))
772 except:
773 pass
774 if xml_dom:
775 xml_dom.unlink()
776
777 def on_player(self, id, msg, xml_dom):
778 act = xml_dom.getAttribute("action")
779 ip = xml_dom.getAttribute("ip")
780 name = xml_dom.getAttribute("name")
781 status = xml_dom.getAttribute("status")
782 version = xml_dom.getAttribute("version")
783 protocol_version = xml_dom.getAttribute("protocol_version")
784 client_string = xml_dom.getAttribute("client_string")
785 try:
786 player = (name,ip,id,status,version,protocol_version,client_string,self.players[id][7])
787 except Exception, e:
788 player = (name,ip,id,status,version,protocol_version,client_string,"Player")
789 if act == "new":
790 self.players[id] = player
791 self.on_player_event(mplay_event(PLAYER_NEW,self.players[id]))
792 elif act == "group":
793 self.group_id = xml_dom.getAttribute("group_id")
794 self.clear_players()
795 self.on_mplay_event(mplay_event(MPLAY_GROUP_CHANGE,self.groups[self.group_id]))
796 self.players[self.id] = self.get_my_info() #(self.name,self.ip,self.id,self.text_status)
797 self.on_player_event(mplay_event(PLAYER_NEW,self.players[self.id]))
798 elif act == "failed":
799 self.on_mplay_event(mplay_event(MPLAY_GROUP_CHANGE_F))
800 elif act == "del":
801 self.on_player_event(mplay_event(PLAYER_DEL,self.players[id]))
802 if self.players.has_key(id):
803 del self.players[id]
804 if id == self.id:
805 self.do_disconnect()
806 # the next two cases handle the events that are used to let you know when others are typing
807 elif act == "update":
808 if id == self.id:
809 self.players[id] = player
810 self.update_self_from_player(player)
811 else:
812 self.players[id] = player
813 dont_send = 0
814 for m in self.ignore_id:
815 if m == id:
816 dont_send=1
817 if dont_send != 1:
818 self.on_player_event(mplay_event(PLAYER_UPDATE,self.players[id]))
819 if xml_dom:
820 xml_dom.unlink()
821
822 def on_password(self, id, msg, xml_dom):
823 signal = type = id = data = None
824 id = xml_dom.getAttribute("id")
825 type = xml_dom.getAttribute("type")
826 signal = xml_dom.getAttribute("signal")
827 data = xml_dom.getAttribute("data")
828 self.on_password_signal( signal,type,id,data )
829 if xml_dom:
830 xml_dom.unlink()
831
832 def check_my_status(self):
833 status = self.get_status()
834 if status == MPLAY_DISCONNECTING:
835 self.do_disconnect()
836
837 def connect(self, addressport):
838 """Establish a connection to a server while still using sendThread & recvThread for its
839 communication."""
840 if self.is_connected():
841 self.log_msg( "Client is already connected to a server?!? Need to disconnect first." )
842 return 0
843 xml_dom = None
844 self.inbox = Queue.Queue(0)
845 self.outbox = Queue.Queue(0)
846 addressport_ar = addressport.split(":")
847 if len(addressport_ar) == 1:
848 address = addressport_ar[0]
849 port = OPENRPG_PORT
850 else:
851 address = addressport_ar[0]
852 port = int(addressport_ar[1])
853 self.host_server = addressport
854 self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
855 try:
856 self.sock.connect((address,port))
857 # send client into with id=0
858 self.sendMsg( self.sock, self.toxml("new") )
859 data = self.recvMsg( self.sock )
860 # get new id and group_id
861 xml_dom = parseXml(data)
862 xml_dom = xml_dom._get_documentElement()
863 self.id = xml_dom.getAttribute("id")
864 self.group_id = xml_dom.getAttribute("group_id")
865 if xml_dom.hasAttribute('useCompression') and xml_dom.getAttribute('useCompression') == 'True':
866 self.useCompression = True
867 if xml_dom.hasAttribute('cmpType'):
868 if cmpBZ2 and xml_dom.getAttribute('cmpType') == 'bz2':
869 self.compressionType = bz2
870 elif cmpZLIB and xml_dom.getAttribute('cmpType') == 'zlib':
871 self.compressionType = zlib
872 else:
873 self.compressionType = None
874 else:
875 self.compressionType = bz2
876 #send confirmation
877 self.sendMsg( self.sock, self.toxml("new") )
878 except Exception, e:
879 self.log_msg(e)
880 if xml_dom:
881 xml_dom.unlink()
882 return 0
883
884 # Start things rollings along
885 self.initialize_threads()
886 self.on_mplay_event(mplay_event(MPLAY_CONNECTED))
887 self.players[self.id] = (self.name,self.ip,self.id,self.text_status,self.version,self.protocol_version,self.client_string,self.role)
888 self.on_player_event(mplay_event(PLAYER_NEW,self.players[self.id]))
889 if xml_dom:
890 xml_dom.unlink()
891 return 1
892
893 def start_disconnect(self):
894 self.on_mplay_event(mplay_event(MPLAY_DISCONNECTING))
895 self.outbox.put( self.toxml("del") )
896 ## Client Side Disconect Forced -- Snowdog 10-09-2003
897 #pause to allow GUI events time to sync.
898 time.sleep(1)
899 self.do_disconnect()
900
901 def do_disconnect(self):
902 client_base.disconnect(self)
903 self.clear_players()
904 self.clear_groups()
905 self.useroles = 0
906 self.on_mplay_event(mplay_event(MPLAY_DISCONNECTED))
907 self.useCompression = False
908
909 def is_connected(self):
910 return (self.status == MPLAY_CONNECTED)
911
912 def get_next_id(self):
913 self.unique_cookie += 1
914 return_str = self.id + "-" + str(self.unique_cookie)
915 return return_str