def buildPenguin(self): self.handshakeStage = -1 self.canRecvPacket = False self.ReceivePacketEnabled = True # Penguin can receive packet only if both this and self.canRecvPacket is true. # Some XT packets are sent before J#JS to make sure client is alive, just to make sure to ignore it ;) # ('category', 'handler', 0 or 1 : execute : don't execute) self.ignorableXTPackets = [('s', 'j#js', 1), ('s', 'p#getdigcooldown', 0), ('s', 'u#h', 0), ('s', 'f#epfgf', 0), ('l', 'login', 1)] self.penguin = PenguinObject() self.penguin.name = None self.penguin.id = None self.penguin.room = None self.penguin.prevRooms = list() self.ref = weakref.proxy(self) # Initiate Packet Handler self.PacketHandler = PacketHandler(self.ref) self.CryptoHandler = Crypto(self.ref)
class Penguin(PenguinDB, ExtensibleObject, LR): ''' AS2 + AS3 Protocol Implementation ''' delimiter = chr(0) def __init__(self, engine): super(Penguin, self).__init__() self.Protocol = engine.server_protocol self.factory = self.engine = engine self.logger = logging.getLogger(TIMELINE_LOGGER) self.cleanConnectionLost = Deferred() self.errored = None self.buildPenguin() def __del__(self): self.logger.warn('Discarding Penguin<%s> Object: %s : %s', self.engine.server_protocol, str(self.client), self.getPortableName()) def buildPenguin(self): self.handshakeStage = -1 self.canRecvPacket = False self.ReceivePacketEnabled = True # Penguin can receive packet only if both this and self.canRecvPacket is true. # Some XT packets are sent before J#JS to make sure client is alive, just to make sure to ignore it ;) # ('category', 'handler', 0 or 1 : execute : don't execute) self.ignorableXTPackets = [('s', 'j#js', 1), ('s', 'p#getdigcooldown', 0), ('s', 'u#h', 0), ('s', 'f#epfgf', 0)] self.penguin = PenguinObject() self.penguin.name = None self.penguin.id = None self.penguin.room = None self.penguin.prevRooms = list() self.selfRefer = weakref.proxy(self) # Initiate Packet Handler self.PacketHandler = PacketHandler(self.selfRefer) self.CryptoHandler = Crypto(self.selfRefer) def initialize(self): self.penguin.nickname = Nickname(self.dbpenguin.nickname, self.selfRefer) self.penguin.swid = self.dbpenguin.swid self.penguin.RefreshHandler = Refresh(self) self.penguin.moderator = int(self.dbpenguin.moderator) self.penguin.stealth_mode = self['moderator'] == 2 self.penguin.mascot_mode = self['moderator'] == 3 self.penguin.x = self.penguin.y = self.penguin.frame = 0 self.penguin.age = Age(self.dbpenguin.create, self.selfRefer) self.penguin.muted = False self.penguin.cache = Cache(self.selfRefer) self.penguin.ninjaHandler = NinjaHandler(self.selfRefer) self.penguin.currencyHandler = CurrencyHandler(self.selfRefer) self.engine.musicHandler.init(self.selfRefer) GeneralEvent('onBuildClient', self.selfRefer) def checkPassword(self, password): return self.CryptoHandler.loginHash() == password @inlineCallbacks def banned(self): bans = yield self.dbpenguin.bans.get( where=['expire > CURRENT_TIMESTAMP'], limit=1) if bans is None: returnValue(False) now = int(time.time()) expire = int(time.mktime(bans.expire.timetuple())) hours = (expire - now) / (60 * 60.0) if 0 < hours < 1: # only minutes left minutes = int(hours * 60) self.send('e', 602, minutes) returnValue(True) if hours <= 0: returnValue(False) # who knows, a millisencond counts too! LOL self.send('e', 601, int(hours)) self.disconnect() returnValue(True) def handleCrossDomainPolicy(self): self.send( "<cross-domain-policy><allow-access-from domain='*' to-ports='{0}' /></cross-domain-policy>" .format(self.engine.port)) # self.disconnect() def getPortableName(self): if self["username"] == None and self["id"] == None: return "{}, {}".format(repr(self.client), self.Protocol) if self["username"] != None: return "{}, {}".format(self["username"], self.Protocol) if self["id"] != None: return "{}, {}".format(self["id"], self.Protocol) return "{}, {}".format(self["username"], self.Protocol) @inlineCallbacks def addItem(self, item, comment="Added via catalog"): if isinstance(item, int): item = self.engine.itemCrumbs[item] elif isinstance(item, str): try: item = self.engine.itemCrumbs[int(item)] except: item = None if item is None: returnValue(False) cost = item.cost if int(self.penguin.coins) < cost: self.send('e', 401) returnValue(False) if self['RefreshHandler'].inInventory(item): returnValue(False) yield Inventory(penguin_id=self['id'], item=int(item), comments=comment).save() yield Coin(penguin_id=self['id'], transaction=-cost, comment="Money spent on adding item ({}). Item: {}".format( comment, int(item))).save() self.penguin.coins -= cost returnValue(True) def __str__(self): ''' AS2 Compatible ''' walking_id = walking_item = walking_type = walking_subtype = '' walking_state = 0 if self['walkingPuffle'] is not None: puffle = self['walkingPuffle'] walking_id = int(puffle.id) walking_item = int(puffle.hat) walking_type = int(puffle.type) walking_subtype = int(puffle.subtype) walking_state = int(puffle.state) data = [ self['id'], self['nickname'], self['language'], self['data'].avatar.color, self['data'].avatar.head, self['data'].avatar.face, self['data'].avatar.neck, self['data'].avatar.body, self['data'].avatar.hand, self['data'].avatar.feet, self['data'].avatar.pin, self['data'].avatar.photo, self['x'], # Cached coordinates self['y'], # Cached coordinates self['frame'], self['member'].rank, # Member/Player rank int(self['member']), # Membership days remaining self['data'].avatar.avatar, # avatar id None, None, # Party Info walking_id, walking_type, walking_subtype, walking_item, walking_state ][:-8 if self.Protocol == AS2_PROTOCOL else None] return '|'.join(map(str, data)) # Easy access to penguin properties def __getitem__(self, prop): return getattr(self.penguin, prop) def __setitem__(self, prop, val): self.penguin[prop] = val def checkForExceptions(self, err): self.errored = err self.engine.log("error", self.getPortableName(), self.errored.getErrorMessage()) def lineReceived(self, line): ''' Extended plugins can overload this function. If you want to override this function, raise NotImplementedError ''' try: super(Penguin, self).lineReceived(line) except NotImplementedError: return receivedPacketDefer = self.PacketHandler.handlePacketReceived(line) receivedPacketDefer.addErrback(self.checkForExceptions) # Defer some other stuff? Probably? def send(self, *args): super(Penguin, self).send(*args) buffers = list(args) if len(buffers) < 1: return if len(buffers) == 1: self.engine.log("debug", "[SEND]", self.getPortableName(), buffers[0]) return self.sendLine(buffers[0]) server_internal_id = "-1" if self.penguin.room != None: server_internal_id = int(self.penguin.room) buffering = ['', PACKET_TYPE] buffering.append(buffers[0]) buffering.append(server_internal_id) buffering += buffers[1:] buffering.append('') buffering = PACKET_DELIMITER.join(list(map(str, buffering))) self.engine.log("debug", "[SEND]", self.getPortableName(), buffering) return self.sendLine(buffering) def log(self, l, *a): self.engine.log(l, self.getPortableName(), *a) def disconnect(self): loseDefer = self.transport.loseConnection() return @inlineCallbacks def connectionLost(self, reason): super(Penguin, self).connectionLost(reason) self.penguin.connectionLost = True # decentralize and make disconnection more flexible if self.engine.type == WORLD_SERVER and self.penguin.id != None: #self.engine.roomHandler.removeFromAnyRoom(self.selfRefer) # needs some fix b4 further usage on-disconnect # sending self just to make sure it doesn't throw weak-reference error if self['RefreshHandler'] is not None: self['RefreshHandler'].RefreshManagerLoop.stop( ) if self['RefreshHandler'].RefreshManagerLoop.running else 0 del self['RefreshHandler'].penguin # remove reference yield self.engine.redis.server.srem( "users:{}".format(self.engine.id), self['swid']) yield GeneralEvent('onClientDisconnect', self) yield self.engine.redis.server.delete("online:{}".format(self['id'])) yield self.engine.disconnect(self) self.cleanConnectionLost.callback(True) def makeConnection(self, transport): self.transport = transport self.client = self.transport self.connectionMade = True self.send( "<cross-domain-policy><allow-access-from domain='*' to-ports='{0}' /></cross-domain-policy>" .format(self.engine.port))
class Penguin(PenguinDB, ExtensibleObject, LR): delimiter = chr(0) def __init__(self, engine): super(Penguin, self).__init__() self.factory = self.engine = engine self.logger = logging.getLogger(TIMELINE_LOGGER) self.cleanConnectionLost = Deferred() self.errored = None self.buildPenguin() def __del__(self): self.logger.warn('Discarding Penguin Object: %s : %s', str(self.client), self.getPortableName()) def buildPenguin(self): self.handshakeStage = -1 self.canRecvPacket = False self.ReceivePacketEnabled = True # Penguin can receive packet only if both this and self.canRecvPacket is true. # Some XT packets are sent before J#JS to make sure client is alive, just to make sure to ignore it ;) # ('category', 'handler', 0 or 1 : execute : don't execute) self.ignorableXTPackets = [('s', 'j#js', 1), ('s', 'p#getdigcooldown', 0), ('s', 'u#h', 0)] self.penguin = PenguinObject() self.penguin.name = None self.penguin.id = None self.penguin.room = None self.penguin.prevRooms = list() self.selfRefer = weakref.proxy(self) # Initiate Packet Handler self.PacketHandler = PacketHandler(self.selfRefer) self.CryptoHandler = Crypto(self.selfRefer) def initialize(self): self.penguin.nickname = Nickname(self.dbpenguin.nickname, self.selfRefer) self.penguin.swid = self.dbpenguin.swid self.penguin.inventory = Inventory(self.selfRefer) self.penguin.inventory.parseFromString(self.dbpenguin.inventory) self.penguin.member = Membership(self.dbpenguin.membership, self.selfRefer) self.penguin.moderator = False #:P self.penguin.x = self.penguin.y = self.penguin.frame = self.penguin.avatar = 0 self.penguin.coins = Coins(self.dbpenguin.coins, self.selfRefer) self.penguin.age = Age(self.dbpenguin.create, self.selfRefer) self.penguin.cache = Cache(self.selfRefer) self.penguin.muted = False self.penguin.epf = EPFAgent(self.dbpenguin.agent, str(self.dbpenguin.epf), self.selfRefer) clothing = [Color, Head, Face, Neck, Body, Hand, Feet, Pin, Photo] for cloth in clothing: name = cloth.__name__.lower() self.penguin[name] = cloth(0, 0, name + " item", False, False, False) self.penguin.mail = MailHandler(self.selfRefer) self.penguin.iglooHandler = PenguinIglooHandler(self.selfRefer) self.penguin.puffleHandler = PuffleHandler(self.selfRefer) self.penguin.stampHandler = StampHandler(self.selfRefer) self.penguin.ninjaHandler = NinjaHandler(self.selfRefer) self.penguin.currencyHandler = CurrencyHandler(self.selfRefer) self.penguin.friendsHandler = FriendsHandler(self.selfRefer) self.engine.musicHandler.init(self.selfRefer) self.loadClothing() GeneralEvent('onBuildClient', self.selfRefer) def loadClothing(self): clothing = [Color, Head, Face, Neck, Body, Hand, Feet, Pin, Photo] for c in clothing: name = c.__name__.lower() db_c = getattr(self.dbpenguin, name) if self.engine.itemCrumbs.itemByIdIsType(db_c, c): self.penguin[name] = self.engine.itemCrumbs[db_c] def checkPassword(self, password): return self.CryptoHandler.loginHash() == password @inlineCallbacks def banned(self): bans = yield Ban.find(where = ['player = ? AND expire > CURRENT_TIMESTAMP', self['id']], limit = 1) if bans is None: returnValue(False) now = int(time.time()) expire = int(time.mktime(bans.expire.timetuple())) hours = (expire - now)/(60*60.0) if 0 < hours < 1: # only minutes left minutes = int(hours * 60) self.send('e', 602, minutes) returnValue(True) if hours <= 0: returnValue(False) # who knows, a millisencond counts too! LOL self.send('e', 601, int(hours)) self.disconnect() returnValue(True) def handleCrossDomainPolicy(self): self.send("<cross-domain-policy><allow-access-from domain='*' to-ports='{0}' /></cross-domain-policy>".format(self.engine.port)) self.disconnect() def getPortableName(self): if self["username"] == None and self["id"] == None: return self.client if self["username"] != None: return self["username"] if self["id"] != None: return self["id"] return self["username"] def addItem(self, item): if isinstance(item, int): item = self.engine.itemCrumbs[item] elif isinstance(item, str): try: item = self.engine.itemCrumbs[int(item)] except: item = None if item is None: return False cost = item.cost if int(self.penguin.coins) < cost: self.send('e', 401) return False if item in self.penguin.inventory: return False self.penguin.inventory.append(item) self.penguin.coins -= cost return True def __str__(self): walking_id = walking_item = walking_type = walking_subtype = '' walking_state = 0 if self['puffleHandler'].walkingPuffle is not None: puffle = self['puffleHandler'].walkingPuffle walking_id = int(puffle.id) walking_item = int(puffle.hat) walking_type = int(puffle.type) walking_subtype = int(puffle.subtype) walking_state = int(puffle.state) data = [ self['id'], self['nickname'], 45, #Language, 45=English self['color'], self['head'], self['face'], self['neck'], self['body'], self['hand'], self['feet'], self['pin'], self['photo'], self['x'], #Cached coordinates self['y'], #Cached coordinates self['frame'], self['member'].rank, #Member/Player rank int(self['member']), #Membership days remaining self['avatar'], #wtf? None, None, #Party Info walking_id, walking_type, walking_subtype, walking_item, walking_state ] return '|'.join(map(str, data)) # Easy access to penguin properties def __getitem__(self, prop): return getattr(self.penguin, prop) def __setitem__(self, prop, val): self.penguin[prop] = val def checkForExceptions(self, err): self.errored = err self.engine.log("error", self.getPortableName(), self.errored.getErrorMessage()) def lineReceived(self, line): ''' Extended plugins can overload this function. If you want to override this function, raise NotImplementedError ''' try: super(Penguin, self).lineReceived(line) except NotImplementedError: return receivedPacketDefer = self.PacketHandler.handlePacketReceived(line) receivedPacketDefer.addErrback(self.checkForExceptions) # Defer some other stuff? Probably? def send(self, *args): super(Penguin, self).send(*args) buffers = list(args) if len(buffers) < 1: return if len(buffers) == 1: self.engine.log("debug", "[SEND]", self.getPortableName(), buffers[0]) return self.sendLine(buffers[0]) server_internal_id = "-1" if self.penguin.room != None: server_internal_id = int(self.penguin.room) buffering = ['', PACKET_TYPE] buffering.append(buffers[0]) buffering.append(server_internal_id) buffering += buffers[1:] buffering.append('') buffering = PACKET_DELIMITER.join(list(map(str, buffering))) self.engine.log("debug", "[SEND]", self.getPortableName(), buffering) return self.sendLine(buffering) def log(self, l, *a): self.engine.log(l, self.getPortableName(), *a) def disconnect(self): loseDefer = self.transport.loseConnection() return @inlineCallbacks def connectionLost(self, reason): del self.PacketHandler.penguin super(Penguin, self).connectionLost(reason) # decentralize and make disconnection more flexible if self.engine.type == WORLD_SERVER and self.penguin.id != None: self.engine.roomHandler.removeFromAnyRoom(self.selfRefer) yield GeneralEvent('onClientDisconnect', self.selfRefer) yield self.engine.disconnect(self) self.cleanConnectionLost.callback(True) def makeConnection(self, transport): self.transport = transport self.client = self.transport self.connectionMade = True self.send("<cross-domain-policy><allow-access-from domain='*' to-ports='{0}' /></cross-domain-policy>".format(self.engine.port))