Package lacewing :: Module server
[frames] | no frames]

Source Code for Module lacewing.server

  1  """ 
  2  The server classes. 
  3  """ 
  4   
  5  import sys 
  6   
  7  from twisted.internet import reactor 
  8  from twisted.internet import protocol 
  9  from twisted.internet.task import LoopingCall 
 10   
 11  from lacewing.protocol import BaseProtocol 
 12  from lacewing.idpool import IDPool 
 13  from lacewing.multidict import MultikeyDict 
 14  from lacewing.packet import ServerPacket, ClientPacket 
 15  from lacewing.packetloaders import client, server 
 16  from lacewing.packetloaders.common import (detectType, CONNECT, SET_NAME,  
 17      JOIN_CHANNEL, LEAVE_CHANNEL, CHANNEL_LIST) 
 18  import lacewing 
 19   
20 -class ServerChannel(object):
21 """ 22 Represents a channel. 23 24 @ivar id: ID of the channel. 25 @ivar name: The name of the channel. 26 @ivar connections: List of the clients in the channel. 27 @ivar autoClose: True if this channel will close after the master leaves 28 @ivar master: The master connection of this channel 29 @type master: L{ServerProtocol} object 30 """ 31 32 id = None 33 name = None 34 hidden = False 35 autoClose = False 36 master = None 37
38 - def __init__(self, name, id, hidden, autoClose, master):
39 self.name = name 40 self.id = id 41 self.hidden = hidden 42 self.autoClose = autoClose 43 self.master = master 44 self.connections = MultikeyDict()
45
46 - def addConnection(self, connection):
47 """ 48 Add a client to the channel, and notify the other clients. 49 50 @param connection: The protocol instance to add. 51 """ 52 53 toClient = server.Peer() 54 toClient.name = connection.name 55 toClient.peer = connection.id 56 toClient.channel = self.id 57 toClient.isMaster = connection is self.master 58 self.sendLoader(toClient) 59 self.connections[connection.name, connection.id] = connection
60
61 - def removeConnection(self, connection):
62 """ 63 Remove a client from the channel, and notify the other 64 clients. 65 66 @param connection: The protocol instance to remove. 67 """ 68 69 # tell the other clients this client is leaving 70 toClient = server.Peer() 71 toClient.peer = connection.id 72 toClient.channel = self.id 73 self.sendLoader(toClient, [connection]) 74 # remove client 75 del self.connections[connection] 76 77 # close the channel if autoClose is set, and the removed client was 78 # the master 79 if connection is self.master and self.autoClose: 80 close = len(self.connections) > 0 81 for connection in self.connections.values(): 82 connection.leaveChannel(self) 83 connection.channelLeft(self) 84 return close 85 86 return len(self.connections) > 0
87
88 - def sendMessage(self, fromConnection, message, subchannel = 0, 89 asObject = False, typeName = None, **settings):
90 """ 91 Send a channel message from the given protocol instance. 92 93 @param fromConnection: The client that the message is sent 94 from. 95 @param message: The message to send. 96 @type message: str/number/ByteReader 97 @param subchannel: The subchannel to send the message on. 98 @type subchannel: number 99 @param typeName: If not specified, the type will be 100 automatically detected (see L{lacewing.packetloaders.common.DATA_TYPES} 101 for possible values) 102 """ 103 if typeName is None: 104 typeName = detectType(message) 105 if asObject: 106 newMessage = server.ObjectChannelMessage() 107 else: 108 newMessage = server.BinaryChannelMessage() 109 newMessage.channel = self.id 110 newMessage.value = message 111 newMessage.subchannel = subchannel 112 newMessage.peer = fromConnection.id 113 newMessage.setDataType(typeName) 114 self.sendLoader(newMessage, [fromConnection], **settings)
115
116 - def sendPrivateMessage(self, message, subchannel, sender, recipient, 117 asObject = False, typeName = None, **settings):
118 """ 119 Send a message from this client to a recipient 120 121 @type message: str/number/ByteReader 122 @param subchannel: The subchannel of the message in the range 0-256 123 @param sender: Sending connection 124 @type sender: L{ServerProtocol} object 125 @param recipient: Connection to send message to 126 @type recipient: L{ServerProtocol} object 127 @param typeName: If not specified, the type will be 128 automatically detected (see L{lacewing.packetloaders.common.DATA_TYPES} 129 for possible values) 130 """ 131 if typeName is None: 132 typeName = detectType(message) 133 if asObject: 134 newMessage = server.ObjectPeerMessage() 135 else: 136 newMessage = server.BinaryPeerMessage() 137 newMessage.channel = self.id 138 newMessage.value = message 139 newMessage.subchannel = subchannel 140 newMessage.peer = sender.id 141 newMessage.setDataType(typeName) 142 recipient.sendLoader(newMessage, **settings)
143
144 - def sendLoader(self, type, notClients = [], **settings):
145 """ 146 Send the specified type to all the clients in the channel, 147 apart from notClients if specified. 148 """ 149 150 toClients = [connection for connection in self.connections.values() 151 if connection not in notClients] 152 153 for client in toClients: 154 client.sendLoader(type, **settings)
155
156 -class ServerProtocol(BaseProtocol):
157 """ 158 The server protocol. 159 @ivar latency: The latency between the server and client. 160 @ivar datagramPort: The UDP port that the connection 161 sends/receives data on. 162 """ 163 _timeoutCall = None 164 165 _currentPing = None 166 _pingTime = None # time when ping was sent 167 latency = None # latency between server and client in seconds 168 169 datagramPort = None 170 _receivePacket = ClientPacket 171
172 - def connectionMade(self):
173 """ 174 When a connection is made, a timeout timer is started, 175 and if the user does not request connection acceptance 176 within reasonable time, disconnect (to prevent flood attacks). 177 """ 178 BaseProtocol.connectionMade(self) 179 timeOut = self.factory.timeOut 180 if timeOut is not None: 181 self._timeoutCall = reactor.callLater(timeOut, self.disconnect, 182 CONNECT, 'Connection timed out')
183
184 - def ping(self, timeOut = None):
185 """ 186 Ping the client with the given timeout. 187 @param timeOut: The number of seconds the client has to 188 respond to the ping. 189 """ 190 if self._pingTime is not None: 191 return 192 newPing = server.Ping() 193 self.sendLoader(newPing) 194 self._pingTime = reactor.seconds() 195 timeOut = timeOut or self.factory.maxPing 196 if timeOut is not None: 197 self._currentPing = reactor.callLater(timeOut, self.disconnect, 198 CONNECT, 'No ping response')
199
200 - def connectionLost(self, reason):
201 """ 202 Once the connection is lost, put back the client ID into the pool, 203 and remove the client from all the channels it has signed on to. 204 """ 205 BaseProtocol.connectionLost(self, reason) 206 factory = self.factory 207 if self._timeoutCall is not None: 208 self._timeoutCall.cancel() 209 self._timeoutCall = None 210 if self._currentPing is not None: 211 self._currentPing.cancel() 212 self._currentPing = None 213 214 for channel in self.channels.values(): 215 self.leaveChannel(channel) 216 if self.isAccepted: 217 factory.userPool.putBack(self.id) 218 del self.factory.connections[self]
219
220 - def loaderReceived(self, loader, isDatagram = False):
221 packetId = loader.id 222 if packetId == client.Request.id: 223 if loader.request == CONNECT: 224 if self.isAccepted: 225 self.disconnect(CONNECT, 'Already accepted') 226 return 227 228 if self._timeoutCall is not None: 229 self._timeoutCall.cancel() 230 self._timeoutCall = None 231 232 factory = self.factory 233 234 if loader.version != self.revision: 235 self.disconnect(CONNECT, 'Invalid revision') 236 return 237 238 # connection checks 239 240 acceptedConnections = [connection for connection in 241 factory.connections.values() if connection.isAccepted] 242 243 if len(acceptedConnections)+1 > self.factory.maxUsers: 244 self.disconnect(CONNECT, 'Server full') 245 return 246 247 # if hook is not interrupted 248 if self.acceptConnection(loader) == False: 249 return 250 self.id = factory.userPool.pop() # select id for this player 251 252 self.factory.connections[(self.id,)] = self 253 254 newWelcome = server.Response() 255 newWelcome.response = CONNECT 256 newWelcome.playerId = self.id 257 newWelcome.success = True 258 newWelcome.welcome = self.factory.getWelcomeMessage(self) 259 self.sendLoader(newWelcome) 260 self.isAccepted = True 261 self.connectionAccepted(loader) 262 263 elif loader.request == SET_NAME: 264 name = loader.name 265 266 if self.acceptLogin(name) == False: 267 return 268 269 if not self.validName(name): 270 self.warn(SET_NAME, 'Invalid name', name = name) 271 return 272 273 for channel in self.channels.values(): 274 if name in channel.connections: 275 self.warn(SET_NAME, 'Name already taken', name = name) 276 return 277 278 if not self.loggedIn: 279 self.setName(name) 280 self.loginAccepted(name) 281 else: 282 self.setName(name) 283 self.nameChanged(name) 284 285 elif loader.request == JOIN_CHANNEL: 286 channelName = loader.name 287 if not self.loggedIn: 288 self.disconnect(JOIN_CHANNEL, 'Name not set', 289 name = channelName) 290 return 291 if channelName in self.channels: 292 self.warn(JOIN_CHANNEL, 'Already part of channel', 293 name = channelName) 294 return 295 if self.acceptChannelJoin(channelName) == False: 296 return 297 if not self.validName(channelName): 298 self.warn(JOIN_CHANNEL, 'Invalid channel name', 299 name = channelName) 300 return 301 try: 302 channel, = self.factory.channels[channelName] 303 if self.name in channel.connections: 304 self.warn(JOIN_CHANNEL, 'Name already taken', 305 name = channelName) 306 return 307 except KeyError: 308 pass 309 flags = loader.flags 310 channel = self.joinChannel(channelName, flags['HideChannel'], 311 flags['AutoClose']) 312 self.channelJoined(channel) 313 314 elif loader.request == LEAVE_CHANNEL: 315 channelId = loader.channel 316 if not channelId in self.channels: 317 self.warn(LEAVE_CHANNEL, 'No such channel', 318 channel = channelId) 319 return 320 channel, = self.channels[channelId] 321 if self.acceptChannelLeave(channel) == False: 322 return 323 self.leaveChannel(channel) 324 self.channelLeft(channel) 325 326 elif loader.request == CHANNEL_LIST: 327 if not self.factory.channelListing: 328 self.disconnect(CHANNEL_LIST, 'Channel listing not enabled') 329 return 330 if self.acceptChannelListRequest() == False: 331 return 332 channels = [] 333 for channel in self.factory.channels.values(): 334 if not channel.hidden: 335 channels.append( 336 (channel.name, len(channel.connections))) 337 self.sendChannelList(channels) 338 self.channelListSent() 339 340 elif packetId in (client.BinaryServerMessage.id, 341 client.ObjectServerMessage.id): 342 self.messageReceived(loader) 343 344 elif packetId in (client.BinaryChannelMessage.id, 345 client.ObjectChannelMessage.id): 346 channelId = loader.channel 347 348 if not channelId in self.channels: 349 return 350 351 channel, = self.channels[channelId] 352 353 if self.acceptChannelMessage(channel, loader) == False: 354 return 355 channel.sendMessage(self, loader.value, loader.subchannel, 356 typeName = loader.getDataType(), asObject = loader.isObject, 357 asDatagram = loader.settings.get('datagram', False)) 358 self.channelMessageReceived(channel, loader) 359 360 elif packetId in (client.BinaryPeerMessage.id, 361 client.ObjectPeerMessage.id): 362 channelId = loader.channel 363 364 if channelId not in self.channels: 365 return 366 367 channel, = self.channels[channelId] 368 try: 369 player, = channel.connections[loader.peer] 370 except KeyError: 371 return 372 if self.acceptPrivateMessage(channel, player, loader) == False: 373 return 374 channel.sendPrivateMessage(loader.value, loader.subchannel, 375 self, player, typeName = loader.getDataType(), 376 asObject = loader.isObject, 377 asDatagram = loader.settings.get('datagram', False)) 378 self.privateMessageReceived(channel, player, loader) 379 380 elif packetId == client.ChannelMaster.id: 381 if not self.factory.masterRights: 382 return 383 channelId = loader.channel 384 if channelId not in self.channels: 385 return 386 channel, = self.channels[channelId] 387 if channel.master is not self: 388 return 389 try: 390 peer, = channel.connections[loader.peer] 391 except KeyError: 392 return 393 action = loader.action 394 if self.acceptMasterAction(channel, peer, action) == False: 395 return 396 if action == client.MASTER_KICK: 397 peer.leaveChannel(channel) 398 self.masterActionExecuted(channel, peer, action) 399 400 elif packetId == client.UDPHello.id and isDatagram: # enable UDP 401 if self.udpEnabled: 402 return 403 self.udpEnabled = True 404 self.sendLoader(server.UDPWelcome()) 405 406 elif packetId == client.Pong.id: # ping response 407 if self._pingTime is None: 408 self.disconnect(CONNECT, 'No pong requested') 409 return 410 if self._currentPing: 411 self._currentPing.cancel() 412 self._currentPing = None 413 self.latency = reactor.seconds() - self._pingTime 414 self._pingTime = None 415 self.pongReceived(self.latency) 416 else: 417 print '(unexpected packet: %r)' % loader 418 self.disconnect() # unexpected packet!
419
420 - def sendLoader(self, loader, asDatagram = False):
421 """ 422 Sends a packetloader to the client 423 @param loader: The packetloader to send. 424 @param asDatagram: True if the packet should be sent as a datagram 425 packet 426 """ 427 # if the sender wants the packet sent as UDP, it has to be 428 # enabled by the server also. 429 if asDatagram: 430 if (self.factory.datagram is not None 431 and self.datagramPort is not None): 432 self.factory.datagram.sendLoader(self, loader) 433 else: 434 newPacket = ServerPacket() 435 newPacket.loader = loader 436 data = newPacket.generate() 437 self.transport.write(data)
438
439 - def disconnect(self, response = None, *arg, **kw):
440 """ 441 Disconnect the the user for a specific reason. 442 """ 443 if response is not None: 444 self.warn(response, **kw) 445 self.transport.loseConnection()
446
447 - def warn(self, response, warning = '', name = None, channel = None):
448 """ 449 Notify the client about a denial. 450 """ 451 newResponse = server.Response() 452 newResponse.response = response 453 newResponse.success = False 454 newResponse.value = warning 455 newResponse.name = name 456 newResponse.channel = channel 457 self.sendLoader(newResponse)
458
459 - def sendMessage(self, message, subchannel, typeName = None, 460 asObject = False, asDatagram = False):
461 """ 462 Send a direct message to the client. 463 464 @type message: str/number/ByteReader 465 @param subchannel: The subchannel of the message in the range 0-256 466 @param typeName: If not specified, the type will be 467 automatically detected (see L{lacewing.packetloaders.common.DATA_TYPES} 468 for possible values) 469 """ 470 if typeName is None: 471 typeName = detectType(message) 472 if asObject: 473 newMessage = server.ObjectServerMessage() 474 else: 475 newMessage = server.BinaryServerMessage() 476 newMessage.value = message 477 newMessage.subchannel = subchannel 478 newMessage.setDataType(typeName) 479 self.sendLoader(newMessage, asDatagram)
480
481 - def leaveChannel(self, channel):
482 """ 483 Remove the client from a specific channel. 484 485 @param channel: The channel to remove the client from. 486 @type channel: ServerChannel object 487 """ 488 factory = self.factory 489 channels = self.channels 490 id = channel.id 491 name = channel.name 492 if not channel.removeConnection(self): 493 factory.destroyChannel(channel) 494 factory.channelPool.putBack(id) 495 del self.channels[channel] 496 newResponse = server.Response() 497 newResponse.success = True 498 newResponse.response = LEAVE_CHANNEL 499 newResponse.channel = channel.id 500 self.sendLoader(newResponse)
501
502 - def joinChannel(self, channelName, hidden = False, autoClose = False):
503 """ 504 Add the client to a new channel. 505 If the channel does not already exist, it is created. 506 507 @param hidden: If this connection is creating the channel, this 508 will hide it from channel listing 509 @param autoClose: If this connection is creating the channel, 510 this will close the channel when the connection leaves 511 """ 512 factory = self.factory 513 newResponse = server.Response() 514 newResponse.response = JOIN_CHANNEL 515 newResponse.success = True 516 newResponse.name = channelName 517 newChannel = self.factory.createChannel(channelName, hidden, autoClose, 518 self) 519 newResponse.channel = newChannel.id 520 newResponse.isMaster = newChannel.master is self 521 for item in newChannel.connections.values(): 522 newResponse.addPeer(item.name, item.id, newChannel.master is item) 523 self.sendLoader(newResponse) 524 newChannel.addConnection(self) 525 self.channels[channelName, newChannel.id] = newChannel 526 return newChannel
527
528 - def setName(self, name):
529 """ 530 Set the name of the client and notify it about it. 531 """ 532 # if already named, remove name from namelist 533 if self in self.factory.connections: 534 del self.factory.connections[self] 535 self.factory.connections[name, self.id] = self 536 self.name = name 537 newResponse = server.Response() 538 newResponse.response = SET_NAME 539 newResponse.success = True 540 newResponse.name = name 541 self.sendLoader(newResponse) 542 self.loggedIn = True 543 544 if self.channels: 545 newClient = server.Peer() 546 newClient.name = name 547 newClient.peer = self.id 548 549 for channel in self.channels.values(): 550 newClient.isMaster = channel.master is self 551 newClient.channel = channel.id 552 del channel.connections[self] 553 channel.connections[self.id, name] = self 554 channel.sendLoader(newClient, [self])
555
556 - def sendChannelList(self, channels):
557 """ 558 Sends a list of channels and their peer count to the other end of 559 the connection. 560 561 @param channels: List of 2-item tuples (channelName, peerCount) 562 """ 563 channelList = server.Response() 564 channelList.response = CHANNEL_LIST 565 channelList.success = True 566 channelList.channels = channels 567 self.sendLoader(channelList)
568
569 - def connectionAccepted(self, welcome):
570 """ 571 Called when the server has accepted the requested connection. 572 @type welcome: L{client.Request} object 573 """
574
575 - def loginAccepted(self, name):
576 """ 577 Called when the server has accepted the requested name. 578 @arg name: The name of the client. 579 @type name: str object 580 """
581
582 - def channelListSent(self):
583 """ 584 Called after a requested channel list is sent. 585 """
586
587 - def nameChanged(self, name):
588 """ 589 Called when the client changed name (after logging in). 590 @type name: str 591 @arg name: The name the client has changed to. 592 """
593
594 - def messageReceived(self, message):
595 """ 596 Called when a client message arrives. 597 @type message: L{client.BinaryServerMessage} or 598 L{client.ObjectServerMessage} 599 """
600
601 - def channelMessageReceived(self, channel, message):
602 """ 603 Called when the client sends a channel message. 604 @arg channel: the channel the user is sending to. 605 @type message: L{client.BinaryChannelMessage} or 606 L{client.ObjectChannelMessage} 607 """
608
609 - def privateMessageReceived(self, channel, recipient, message):
610 """ 611 Called when the client has sent a private message 612 @arg channel: the channel the user is sending from. 613 @type message: L{client.BinaryPeerMessage} or 614 L{client.ObjectPeerMessage} 615 @type recipient: L{ServerProtocol} object 616 """
617
618 - def channelJoined(self, channel):
619 """ 620 Called when the client has joined a new channel. 621 @arg channel: The channel the client has joined. 622 """
623
624 - def channelLeft(self, channel):
625 """ 626 Called when the client has left a channel. 627 @arg channel: The channel the client has left. 628 """
629
630 - def pongReceived(self, latency):
631 """ 632 Called when a ping response has been received. 633 @arg latency: Round-trip time in seconds between the client and 634 the server. 635 """
636
637 - def masterActionExecuted(self, channel, peer, action):
638 """ 639 Called when after a master action has been executed. 640 @arg channel: The channel the action took place in 641 @arg peer: The peer the action was executed on 642 @arg action: The action the master requested 643 @type action: Currently, only {pylacewing.constants.MASTER_KICK} 644 """
645 646 # Server hooks, so you can abort or cancel an action 647
648 - def acceptLogin(self, name):
649 """ 650 Cancels the client login if False is returned. 651 Useful if the server specifies client names. 652 @arg name: The requested client name. 653 @return: Return False to cancel client login. 654 @rtype: bool 655 """
656
657 - def acceptNameChange(self, name):
658 """ 659 Cancels the client name change if False is returned. 660 Useful if the server specifies client names. 661 @arg name: The requested client name. 662 @return: Return False to cancel name change. 663 @rtype: bool 664 """
665
666 - def acceptConnection(self, welcome):
667 """ 668 Cancels the connection accept response if False is returned. 669 Useful for banning. 670 @type welcome: L{client.Request} object 671 @return: Return False to cancel connection acceptance. 672 @rtype: bool 673 """
674
675 - def acceptChannelMessage(self, channel, message):
676 """ 677 Cancels the channel message if False is returned. 678 Useful for blocking invalid messages. 679 @type message: L{client.BinaryChannelMessage} or 680 L{client.ObjectChannelMessage} 681 @return: Return False to stop the channel message 682 before it is sent. 683 @rtype: bool 684 """
685
686 - def acceptPrivateMessage(self, channel, recipient, message):
687 """ 688 Cancels the private message if False is returned. 689 Useful for blocking invalid messages. 690 @arg channel: the channel the user sending from. 691 @type message: L{client.BinaryPeerMessage} or 692 L{client.ObjectPeerMessage} 693 @type recipient: L{ServerProtocol} object 694 @return: Return False to stop the channel message 695 before it is sent. 696 @rtype: bool 697 """
698
699 - def acceptChannelListRequest(self):
700 """ 701 Cancels the sending of the channel list 702 if False is returned. 703 Useful for servers with a strict policy. 704 @return: Return False to send no channel list. 705 @rtype: bool 706 """
707
708 - def acceptChannelJoin(self, channelName):
709 """ 710 Cancels the channel join accept response if False is returned. 711 Useful for channel-less servers. 712 @arg channelName: The name of the channel. 713 @return: Return False to cancel channel join acceptance. 714 @rtype: bool 715 """
716
717 - def acceptChannelLeave(self, channel):
718 """ 719 Cancels the channel leave accept response if False is returned. 720 Useful if the client may not leave the current channel. 721 @arg channel: The channel the client is leaving. 722 @return: Return False to cancel channel join acceptance. 723 @rtype: bool 724 """
725
726 - def acceptMasterAction(self, channel, peer, action):
727 """ 728 Cancels a master action if False is returned. 729 Useful for blocking some specific master actions. 730 @arg channel: The channel the action is taking place in. 731 @arg peer: The peer this action is executed on. 732 @arg action: The action the master has requested. 733 @type action: Currently, only {pylacewing.constants.MASTER_KICK} 734 @rtype: bool 735 """
736
737 -class ServerDatagram(protocol.DatagramProtocol):
738 - def __init__(self, factory):
739 """ 740 @param factory: The master TCP factory of the server. 741 """ 742 self.factory = factory 743 factory.datagram = self
744
745 - def datagramReceived(self, data, (host, port)):
746 newPacket = ClientPacket(data, datagram = True) 747 playerId = newPacket.fromId 748 try: 749 connection, = self.factory.connections[playerId] 750 if (not connection.isAccepted 751 or connection.transport.getPeer().host != host): 752 return 753 connection.datagramPort = port 754 connection.loaderReceived(newPacket.loader, True) 755 except (ValueError, KeyError): 756 pass
757
758 - def sendLoader(self, connection, loader):
759 address = (connection.transport.getPeer().host, connection.datagramPort) 760 newPacket = ServerPacket(datagram = True) 761 newPacket.loader = loader 762 self.transport.write(newPacket.generate(), address)
763
764 -class ServerFactory(protocol.ServerFactory):
765 """ 766 The server factory. 767 768 @ivar channelClass: This is the channel class that will be used when 769 creating a new channel. Subclass L{ServerChannel} and replace this 770 attribute if you want to change the behaviour of channels. 771 @ivar maxPing: This is the time the client has to respond 772 to pings (in seconds). Can be a number or None for no max ping (default) 773 @ivar pingTime: The interval between pings in seconds 774 @ivar maxUsers: The max number of users allowed on the server. 775 @ivar timeOut: The number of seconds the client has to send a Hello packet 776 before being disconnected. Can be a number or None for no timeout 777 @ivar welcomeMessage: The message sent to accepted clients. 778 @ivar ping: If True, pinging will be enabled on the server 779 @ivar channelListing: If True, channelListing is enabled on the server 780 @ivar masterRights: If True, this enables the autoclose feature for 781 clients when creating channels 782 """ 783 channelClass = ServerChannel 784 timeOut = 8 785 pingTime = 8 786 maxPing = None 787 maxUsers = 1000 788 welcomeMessage = 'Welcome! Server is running pylacewing %s (%s)' % ( 789 lacewing.__version__, sys.platform) 790 791 datagram = None 792 ping = True 793 channelListing = True 794 masterRights = False 795 796 _pinger = None 797
798 - def startFactory(self):
799 self.connections = MultikeyDict() 800 self.channels = MultikeyDict() 801 self.userPool = IDPool() 802 self.channelPool = IDPool() 803 if self.ping: 804 self._pinger = pinger = LoopingCall(self.globalPing) 805 pinger.start(self.pingTime, False)
806
807 - def globalPing(self):
808 """ 809 Pings all clients currently connected to the server 810 """ 811 for connection in self.connections.values(): 812 connection.ping()
813
814 - def getWelcomeMessage(self, connection):
815 """ 816 This method is called when a connection has been accepted, and 817 a welcome message has to be sent. The default implementation just 818 returns L{welcomeMessage}, but override this method to change that 819 behaviour. 820 @param connection: Connection that has been accepted 821 @type connection: L{ServerProtocol} object 822 @rtype: str 823 """ 824 return self.welcomeMessage
825
826 - def createChannel(self, name, hidden, autoClose, master):
827 if not self.masterRights: 828 autoClose = False 829 try: 830 channel, = self.channels[name] 831 except KeyError: 832 id = self.channelPool.pop() 833 channel = self.channelClass(name, id, hidden, autoClose, master) 834 self.channels[name, id] = channel 835 self.channelAdded(channel) 836 return channel
837
838 - def destroyChannel(self, channel):
839 del self.channels[channel] 840 self.channelRemoved(channel)
841
842 - def channelRemoved(self, channel):
843 """ 844 Called when a channel has no users in it, and 845 is therefore removed. 846 @arg channel: The channel that is being removed. 847 """
848
849 - def channelAdded(self, channel):
850 """ 851 Called when a new channel is created. 852 @arg channel: The channel that has been created. 853 """
854