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

Source Code for Module lacewing.client

  1  """ 
  2  The client classes. 
  3  """ 
  4   
  5  from time import time 
  6   
  7  from twisted.internet import reactor, defer 
  8  from twisted.internet import protocol 
  9  from twisted.internet.task import LoopingCall 
 10   
 11  from lacewing.protocol import BaseProtocol 
 12  from lacewing.packet import ServerPacket, ClientPacket 
 13  from lacewing.packetloaders import server, client 
 14  from lacewing.multidict import MultikeyDict 
 15  from lacewing.packetloaders.common import (detectType, CONNECT, SET_NAME,  
 16      JOIN_CHANNEL, LEAVE_CHANNEL, CHANNEL_LIST) 
 17   
18 -class Peer(object):
19 """ 20 Represents a remote client. 21 @ivar name: The name of the client. 22 @ivar id: The ID of the client 23 @ivar master: True if this peer is the master of the channel 24 """ 25 user = None 26 name = None 27 id = None 28 master = False 29
30 - def __init__(self, name, id, master, channel):
31 self.name = name 32 self.id = id 33 self.master = master 34 self.channel = channel 35 self.user = channel.user
36
37 - def sendMessage(self, message, subchannel, typeName = None, 38 asObject = False, asDatagram = False):
39 """ 40 Send a message to another peer 41 42 @type message: str/number/ByteReader 43 @param subchannel: The subchannel of the message in the range 0-256 44 @param typeName: If not specified, the type will be 45 automatically detected (see L{lacewing.packetloaders.common.DATA_TYPES} 46 for possible values) 47 """ 48 if asObject: 49 newMessage = client.ObjectPeerMessage() 50 else: 51 newMessage = client.BinaryPeerMessage() 52 newMessage.value = message 53 newMessage.subchannel = subchannel 54 newMessage.channel = self.channel.id 55 newMessage.peer = self.id 56 newMessage.setDataType(typeName or detectType(message)) 57 self.user.sendLoader(newMessage, asDatagram)
58
59 - def kick(self, peer):
60 pass
61
62 -class ClientChannel(object):
63 """ 64 Represents a server channel. 65 @ivar id: The ID of the channel. 66 @ivar name: The name of the channel. 67 @ivar connections: A dict of signed on connections. 68 """ 69 user = None 70 71 id = None 72 name = None 73
74 - def __init__(self, user):
75 self.connections = MultikeyDict() 76 self.user = user
77
78 - def sendMessage(self, message, subchannel, typeName = None, asObject = False, 79 asDatagram = False):
80 """ 81 Send a message to the channel 82 83 @type message: str/number/ByteReader 84 @param subchannel: The subchannel of the message in the range 0-256 85 @param typeName: If not specified, the type will be 86 automatically detected (see L{lacewing.packetloaders.common.DATA_TYPES} 87 for possible values) 88 """ 89 if typeName is None: 90 typeName = detectType(message) 91 if asObject: 92 newMessage = client.ObjectChannelMessage() 93 else: 94 newMessage = client.BinaryChannelMessage() 95 newMessage.value = message 96 newMessage.subchannel = subchannel 97 newMessage.channel = self.id 98 newMessage.setDataType(typeName) 99 self.user.sendLoader(newMessage, asDatagram)
100
101 -class ClientProtocol(BaseProtocol):
102 """ 103 The client protocol. 104 105 @ivar datagram: The ClientDatagram protocol this connection is using for 106 UDP handling 107 """ 108 datagram = None 109 _udpCheck = None # LoopingCall for checking if UDP enabled 110 udpTimeout = 3000 111 _loginName = None 112 113 _channelList = None 114 _channelListDefer = None 115 _receivePacket = ServerPacket 116
117 - def connectionMade(self):
118 """ 119 Sends a connection acceptance request to the server 120 after the connection has been made. 121 """ 122 BaseProtocol.connectionMade(self) 123 self._udpCheck = LoopingCall(self._requestUDP) 124 self._channelList = [] 125 self._channelListDefer = [] 126 hello = client.Request() 127 hello.request = CONNECT 128 hello.version = self.revision 129 self.sendLoader(hello)
130
131 - def loaderReceived(self, loader, isDatagram = False):
132 packetId = loader.id 133 if packetId == server.Response.id: 134 if not loader.success: 135 self.serverDenied(loader) 136 return 137 response = loader.response 138 if response == CONNECT: 139 datagram = ClientDatagram(self) 140 reactor.listenUDP(0, datagram) 141 self.datagram = datagram 142 self.serverGreeted(loader.welcome) 143 self.id = loader.playerId 144 self._enableUDP() 145 146 elif response == SET_NAME: 147 name = self.name = loader.name 148 if self.loggedIn: 149 self.nameChanged(name) 150 else: 151 self.loggedIn = True 152 self.loginAccepted(self.name) 153 154 elif response == JOIN_CHANNEL: 155 channelId = loader.channel 156 channelName = loader.name 157 channel = ClientChannel(self) 158 channel.id = channelId 159 channel.name = channelName 160 self.channels[channelId, channelName] = channel 161 connections = channel.connections 162 for peer in loader.peers: 163 clientId = peer.id 164 name = peer.name 165 peer = Peer(name, clientId, peer.isMaster, channel) 166 self.channelUserExists(channel, peer) 167 connections[clientId, name] = peer 168 self.channelJoined(channel) 169 170 elif response == LEAVE_CHANNEL: 171 channel, = self.channels[loader.channel] 172 del self.channels[channel] 173 self.channelLeft(channel) 174 175 elif response == CHANNEL_LIST: 176 self._channelListDefer.pop(0).callback(loader.channels) 177 return 178 179 elif packetId == server.UDPWelcome.id and not self.udpEnabled: 180 self._udpCheck.stop() 181 self.isAccepted = True 182 self.connectionAccepted() 183 184 elif packetId in (server.BinaryServerMessage.id, 185 server.ObjectServerMessage.id): 186 self.messageReceived(loader) 187 188 elif packetId in (server.BinaryChannelMessage.id, 189 server.ObjectChannelMessage.id): 190 channel, = self.channels[loader.channel] 191 sender, = channel.connections[loader.peer] 192 self.channelMessageReceived(channel, sender, loader) 193 194 elif packetId == server.Ping.id: 195 self.sendLoader(client.Pong()) 196 self.pingReceived() 197 198 elif packetId == server.Peer.id: 199 channel, = self.channels[loader.channel] 200 connections = channel.connections 201 clientId = loader.peer 202 name = loader.name 203 if name is not None: 204 if not clientId in connections: 205 # create a new client instance 206 peer = Peer(name, clientId, loader.isMaster, channel) 207 self.channelUserJoined(channel, peer) 208 else: 209 peer, = channel.connections[clientId] 210 del channel.connections[peer] 211 peer.name = name 212 peer.master = loader.isMaster 213 self.channelUserChanged(channel, peer) 214 connections[clientId, name] = peer 215 else: 216 peer, = channel.connections[clientId] 217 self.channelUserLeft(channel, peer) 218 del connections[peer] 219 220 elif packetId in (server.BinaryPeerMessage.id, 221 server.ObjectPeerMessage.id): 222 channel, = self.channels[loader.channel] 223 peer, = channel.connections[loader.peer] 224 self.privateMessageReceived(channel, peer, loader) 225 226 else: 227 # packet type not known 228 raise NotImplementedError('unknown packet type (%s)' % packetId)
229
230 - def sendLoader(self, loader, asDatagram = False):
231 """ 232 Sends a packetloader to the server 233 @param loader: The packetloader to send. 234 """ 235 if asDatagram: 236 self.datagram.sendLoader(loader) 237 else: 238 newPacket = ClientPacket() 239 newPacket.loader = loader 240 self.transport.write(newPacket.generate())
241
242 - def _enableUDP(self):
243 self._udpCheck.start(self.udpTimeout / 1000)
244
245 - def _requestUDP(self):
246 self.sendLoader(client.UDPHello(), True)
247
248 - def requestChannelList(self):
249 """ 250 Request a channel list from the server. 251 252 @return: A deferred that will fire with 253 a list of ChannelList objects 254 """ 255 d = defer.Deferred() 256 self._channelListDefer.append(d) 257 request = client.Request() 258 request.request = CHANNEL_LIST 259 self.sendLoader(request) 260 return d
261
262 - def sendMessage(self, message, subchannel, typeName = None, 263 asObject = False, asDatagram = False):
264 """ 265 Send a message directly to the server. 266 @param message: The message to send. 267 @type message: str/number/ByteReader 268 @param subchannel: A subchannel in the range 0-256 269 @param typeName: If not specified, the type will be 270 automatically detected (see L{lacewing.packetloaders.common.DATA_TYPES} 271 for possible values) 272 """ 273 if typeName is None: 274 typeName = detectType(message) 275 if asObject: 276 newMessage = client.ObjectServerMessage() 277 else: 278 newMessage = client.BinaryServerMessage() 279 newMessage.value = message 280 newMessage.subchannel = subchannel 281 newMessage.setDataType(typeName) 282 self.sendLoader(newMessage, asDatagram)
283
284 - def joinChannel(self, channelName, hidden = False, autoClose = False):
285 """ 286 Join a channel by name. 287 288 @param hidden: If this connection is creating the channel, this will 289 hide the channel from channel listing 290 @param autoClose: If this connection is creating the channel and if 291 master rights are enabled on the server, this will close the 292 channel when the client leaves 293 """ 294 request = client.Request() 295 request.request = JOIN_CHANNEL 296 request.name = channelName 297 request.flags['HideChannel'] = hidden 298 request.flags['AutoClose'] = autoClose 299 self.sendLoader(request)
300
301 - def leaveChannel(self, channel):
302 """ 303 Leave a channel. 304 @param channel: The channel to leave. 305 @type channel: str, number, or OChannel object 306 """ 307 channel, = self.channels[channel] 308 request = client.Request() 309 request.request = LEAVE_CHANNEL 310 request.channel = channel.id 311 self.sendLoader(request)
312
313 - def setName(self,name):
314 """ 315 Request a name change. 316 @param name: The name to request. 317 @type name: str 318 """ 319 newName = client.Request() 320 newName.request = SET_NAME 321 newName.name = name 322 self.sendLoader(newName)
323 324 # events specific to the client 325
326 - def serverGreeted(self, welcome):
327 """ 328 Called when the server has responded to our welcome. 329 """
330
331 - def connectionAccepted(self):
332 """ 333 Called when the server has accepted the requested connection. 334 """
335
336 - def loginAccepted(self, name):
337 """ 338 Called when the server has accepted the requested name 339 (or has specified one itself). 340 @arg name: The name of the client. 341 @type name: str object 342 """
343
344 - def nameChanged(self, name):
345 """ 346 Called when the server has accepted name change 347 (or has given the client a new one). 348 @type name: str 349 @arg name: The new name for the client. 350 """
351
352 - def serverDenied(self, response):
353 """ 354 Called when the server disconnects or when the 355 server denies an client request. 356 @arg response: The response of the action. 357 """
358
359 - def messageReceived(self, message):
360 """ 361 Called when a server message arrives. 362 @type message: L{server.BinaryServerMessage} or 363 L{server.ObjectServerMessage} 364 """
365
366 - def channelMessageReceived(self, channel, user, message):
367 """ 368 Called when a message from a channel arrives. 369 @arg channel: the channel the message came from. 370 @arg user: the sender of the message. 371 @type message: L{server.BinaryChannelMessage} or 372 L{server.ObjectChannelMessage} 373 """
374
375 - def privateMessageReceived(self, channel, sender, message):
376 """ 377 Called when a private message has been received 378 @arg channel: the channel the user is sending from. 379 @type message: L{server.BinaryChannelMessage} or 380 L{server.ObjectChannelMessage} 381 @type sender: L{Peer} object 382 """
383
384 - def channelJoined(self, channel):
385 """ 386 Called when the server has accepted a channel join request. 387 @arg channel: The channel the client has joined. 388 """
389
390 - def channelLeft(self, channel):
391 """ 392 Called when the server has accepted a channel leave request. 393 @arg channel: The channel the client has left. 394 """
395
396 - def channelUserJoined(self, channel, client):
397 """ 398 Called when a client has joined the channel. 399 @arg channel: The channel the client has joined. 400 """
401
402 - def channelUserExists(self, channel, client):
403 """ 404 Called when a client exists in the channel. 405 @arg channel: The channel the client exists in. 406 """
407
408 - def channelUserLeft(self, channel, client):
409 """ 410 Called when a client has left the channel. 411 @arg channel: The channel the client has left. 412 """
413
414 - def channelUserChanged(self, channel, client):
415 """ 416 Called when a client in the channel has changed name. 417 @arg channel: The channel the client resides in. 418 """
419
420 - def pingReceived(self):
421 """ 422 Called when a server ping has been received. 423 """
424
425 -class ClientDatagram(protocol.DatagramProtocol):
426 connection = None 427 host = None 428 port = None
429 - def __init__(self, connection):
430 """ 431 @param connection: The ClientProtocol instance this datagram should use 432 """ 433 self.connection = connection 434 port = connection.transport.getPeer() 435 self.host, self.port = port.host, port.port
436
437 - def startProtocol(self):
438 self.transport.connect(self.host, self.port)
439
440 - def datagramReceived(self, data, (host, port)):
441 newPacket = ServerPacket(data, datagram = True) 442 self.connection.loaderReceived(newPacket.loader, True)
443
444 - def sendLoader(self, loader):
445 newPacket = ClientPacket(datagram = True) 446 newPacket.fromId = self.connection.id 447 newPacket.loader = loader 448 self.transport.write(newPacket.generate())
449