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

Source Code for Module lacewing.server

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