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

Source Code for Module lacewing.moo.server

  1  # Copyright (c) 2011 Mathias Kaerlev. 
  2  # See LICENSE for details. 
  3   
  4  from twisted.internet.protocol import ServerFactory 
  5   
  6  from lacewing.moo.protocol import MooProtocol 
  7   
  8  from lacewing.moo.packet import ServerPacket, ClientPacket 
  9   
 10  from lacewing.moo.packetloaders.server import * 
 11  from lacewing.moo.packetloaders.client import * 
 12   
 13  from lacewing.multidict import MultikeyDict 
 14   
 15  from lacewing.moo.packetloaders.message import * 
 16   
 17  from lacewing.idpool import IDPool 
 18   
19 -class Channel(object):
20 name = None 21 id = None 22 factory = None 23 24 connections = None 25
26 - def __init__(self, name, id, factory):
27 self.name = name 28 self.id = id 29 self.factory = factory 30 31 self.connections = MultikeyDict()
32
33 - def addConnection(self, connection):
34 self.connections[connection.name, connection.id] = connection 35 connection.channels[self.name, self.id] = self 36 37 newJoined = ChannelJoined() 38 newJoined.setChannel(self) 39 newJoined.setConnection(connection) 40 connection.sendLoader(newJoined) 41 42 newExists = PlayerExists() 43 newExists.setChannel(self) 44 45 newJoined = PlayerJoined() 46 newJoined.setChannel(self) 47 newJoined.setConnection(connection) 48 49 for otherConnection in self.connections.values(): 50 if otherConnection != connection: 51 newExists.setConnection(otherConnection) 52 connection.sendLoader(newExists) 53 otherConnection.sendLoader(newJoined)
54
55 - def removeConnection(self, connection):
56 newLeft = PlayerLeft() 57 newLeft.setChannel(self) 58 newLeft.setConnection(connection) 59 self.sendLoader(newLeft) 60 61 del self.connections[connection] 62 del connection.channels[self] 63 64 return len(self.connections) != 0
65
66 - def sendMessage(self, connection, value, subchannel, type = None, toClient = None):
67 newValue = Message(**connection.settings) 68 newValue.type = type or detectType(value) 69 newValue.value = value 70 newMessage = FromChannelMessage() 71 newMessage.setChannel(self) 72 newMessage.setConnection(connection) 73 newMessage.message = newValue 74 newMessage.subchannel = subchannel 75 if toClient: 76 toClient.sendLoader(newMessage) 77 else: 78 self.sendLoader(newMessage, connection)
79
80 - def sendLoader(self, loader, fromClient = None):
81 for connection in self.connections.values(): 82 if connection != fromClient: 83 connection.sendLoader(loader)
84
85 - def getMaster(self):
86 return self.connections.values()[0]
87
88 -class MooServerProtocol(MooProtocol):
89 _sendPacket = ServerPacket 90 _receivePacket = ClientPacket 91
92 - def connectionMade(self):
93 self.settings = self.factory.settings 94 if not self.acceptConnection(): 95 return 96 newMotd = MOTD() 97 newMotd.motd = self.factory.motd 98 self.sendLoader(newMotd) 99 newAssigned = AssignedID() 100 newId = self.factory.userPool.popId() 101 self.id = newId 102 newAssigned.playerId = newId 103 self.sendLoader(newAssigned) 104 self.isAccepted = True 105 self.connectionAccepted()
106
107 - def connectionLost(self, reason):
108 for channel in self.channels.values(): 109 self.leaveChannel(channel) 110 self.factory.userPool.putBackId(self.id)
111
112 - def loaderReceived(self, loader):
113 if isinstance(loader, SetName): 114 self.name = loader.playerName 115 self.nameSet(self.name) 116 117 elif not self.isAccepted or not self.name: 118 # if the client has come this far, 119 # it means it is sending messages 120 # without getting accepted 121 # or setting name 122 self.transport.loseConnection() 123 124 elif isinstance(loader, ClientMessage): 125 self.messageReceived(loader.message, loader.subchannel) 126 127 elif isinstance(loader, JoinChannel): 128 channelName = loader.channelName 129 if channelName in self.channels: 130 # client is in the channel already, 131 # so ignore 132 return 133 if not self.acceptChannelJoin(channelName): 134 return 135 self.joinChannel(channelName) 136 137 elif isinstance(loader, LeaveChannel): 138 channelId = loader.channelId 139 if not channelId in self.channels: 140 # the client isn't already 141 # in the channel, so we just 142 # ignore 143 return 144 channel, = self.channels[channelId] 145 if not self.acceptChannelLeave(channel): 146 return 147 self.leaveChannel(channel) 148 149 elif isinstance(loader, ToChannelMessage): 150 channelId = loader.channelId 151 if not channelId in self.channels: 152 # client has not 153 # joined a channel with 154 # this id-- ignore. 155 return 156 channel, = self.channels[channelId] 157 message = loader.message 158 subchannel = loader.subchannel 159 if not self.acceptChannelMessage(channel, message, subchannel): 160 return 161 channel.sendMessage(self, message.value, subchannel, message.type) 162 self.channelMessageReceived(channel, message, subchannel) 163 164 elif isinstance(loader, PrivateMessage): 165 channelId = loader.channelId 166 if not channelId in self.channels: 167 # client has not 168 # joined a channel with 169 # this id-- ignore. 170 return 171 channel, = self.channels[channelId] 172 playerId = loader.playerId 173 if not playerId in channel.connections: 174 # player is not to be found in the 175 # channel 176 return 177 player, = channel.connections[playerId] 178 message = loader.message 179 subchannel = loader.subchannel 180 if not self.acceptPrivateMessage(channel, player, message, subchannel): 181 return 182 channel.sendMessage(self, message.value, subchannel, message.type, player) 183 self.privateMessageReceived(channel, player, message, subchannel) 184 185 elif isinstance(loader, ChangeName): 186 if not self.acceptNameChange(self.name, loader.newName): 187 return 188 self.changeName(loader.newName) 189 190 else: 191 raise NotImplementedError
192 193 # accept-like 194
195 - def acceptConnection(self):
196 """ 197 Return False to stop the server from sending 198 a welcome to the client 199 """ 200 return True
201
202 - def acceptNameChange(self, oldName, newName):
203 """ 204 Return False to decline the client to change 205 it's name (after setting the inital name) 206 """ 207 return True
208
209 - def acceptChannelJoin(self, channelName):
210 """ 211 Return False to stop the client from joining the specified 212 channel 213 """ 214 return True
215
216 - def acceptChannelMessage(self, channel, message, subchannel):
217 """ 218 Return False to stop the channel message from being 219 sent to the other channel members 220 """ 221 return True
222
223 - def acceptPrivateMessage(self, channel, connection, message, subchannel):
224 """ 225 Return False to stop the private message from reaching 226 the recipient 227 """ 228 return True
229
230 - def acceptChannelLeave(self, channel):
231 """ 232 Return False to stop the client from leaving the specified 233 channel 234 """ 235 return True
236 237 # event-like 238
239 - def connectionAccepted(self):
240 """ 241 Called when the connection has been accepted, and the 242 client notified. 243 """
244
245 - def nameSet(self, name):
246 """ 247 Called after the name has been set 248 """
249
250 - def nameChanged(self, oldName, newName):
251 """ 252 Called when the client has changed name 253 """
254
255 - def messageReceived(self, message, subchannel):
256 """ 257 Called upon receiving a client message. 258 259 @type message: L{Message} object 260 """
261
262 - def channelJoined(self, channel):
263 """ 264 Called when the client has been let into 265 a channel 266 """
267
268 - def channelMessageReceived(self, channel, message, subchannel):
269 """ 270 Called after a channel message has been relayed. 271 272 @type message: lacewing.moo.packetloaders.message.Message object 273 """
274
275 - def privateMessageReceived(self, channel, player, message, subchannel):
276 """ 277 Called after a private message has been relayed. 278 279 @type message: lacewing.moo.packetloaders.message.Message object 280 """
281
282 - def channelLeft(self, channel):
283 """ 284 Called when the client has left a channel 285 """
286
287 - def getHost(self):
288 """ 289 This method returns the IP address of the connection. 290 Override if you would like to hide/edit the IP 291 """ 292 return self.transport.getPeer().host
293 294 # action-like 295
296 - def changeName(self, name):
297 oldName = self.name 298 299 self.name = name 300 301 newChanged = PlayerChanged() 302 newChanged.setConnection(self) 303 304 for channel in self.channels.values(): 305 channel.sendLoader(newChanged) 306 307 self.nameChanged(oldName, name)
308
309 - def sendMessage(value, subchannel, type = None):
310 """ 311 Sends a message to the other end 312 """ 313 newValue = Message(**self.settings) 314 newValue.type = type or detectType(value) 315 newValue.value = value 316 newMessage = FromChannelMessage() 317 newMessage.setServer() 318 newMessage.message = newValue 319 newMessage.subchannel = subchannel 320 self.sendLoader(newMessage)
321
322 - def sendChannelMessage(self, channel, *arg, **kw):
323 channel.sendMessage(self, *arg, **kw)
324
325 - def joinChannel(self, channelName):
326 newChannel = self.factory.createChannel(channelName) 327 newChannel.addConnection(self) 328 self.channelJoined(newChannel)
329
330 - def leaveChannel(self, channel):
334
335 -class MooServerFactory(ServerFactory):
336 motd = '' 337 338 channels = None 339 connections = None 340 341 channelPool = None 342 userPool = None 343
344 - def __init__(self, **settings):
345 self.settings = settings 346 self.channels = MultikeyDict() 347 self.connections = MultikeyDict() 348 self.userPool = IDPool(1) 349 self.channelPool = IDPool(1)
350
351 - def createChannel(self, channelName):
352 try: 353 channel, = self.channels[channelName] 354 return channel 355 except KeyError: 356 newId = self.channelPool.popId() 357 newChannel = Channel(channelName, newId, self) 358 self.channels[channelName, newId] = newChannel 359 self.channelCreated(newChannel) 360 return newChannel
361
362 - def destroyChannel(self, channel):
363 del self.channels[channel] 364 self.channelDestroyed(channel)
365
366 - def channelCreated(self, channel):
367 """ 368 Called when a new channel is 369 created 370 """
371
372 - def channelDestroyed(self, channel):
373 """ 374 Called when the channel is destroyed because 375 it is now empty 376 """
377