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

Source Code for Module lacewing.client

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