def turn(self, direction): assert direction < 4 if self.direction == direction: return if not self.alive: #or not self.actionLock(self.turn, direction): return self.direction = direction self.lastAction = time.time() + 0.15 # Make package for spectator in getSpectators(self.position): stream = spectator.packet(0x6B) stream.position(self.position) stream.uint8(getTile(self.position).findStackpos(self)) stream.uint16(0x63) stream.uint32(self.clientId()) stream.uint8(direction) if spectator.version >= 953: stream.uint8(self.solid) stream.send(spectator)
def move(self, direction, spectators=None, level=0, stopIfLock=False, failback=None, push=True, ignorestairhop=False): if not self.alive: return -1 elif not self.data["health"] or not self.canMove or not self.speed: return False _time = time.time() # Don't use actionLock (TODO: Kill?) because we might get False, which would prevent us from moving later. if self.lastAction > _time: if not stopIfLock: call_later(self.lastAction - _time, self.move, direction, spectators, level, stopIfLock, failback, push, ignorestairhop) return False # Stairhop delay. isPlayer = self.isPlayer() if not ignorestairhop and isPlayer and _time - self.lastStairHop < config.stairHopDelay and level: return False oldPosition = self.position.copy() # Drunk? if self.hasCondition(CONDITION_DRUNK): directions = [ 0, 1, 2, 3, 4, 5, 6, 7, direction ] # Double the odds of getting the correct direction. directions.remove(self.reverseDirection( )) # From a reality perspective, you are rarely that drunk. direction = random.choice(directions) # Recalculate position position = oldPosition.copy() if direction == 0: position.y -= 1 elif direction == 1: position.x += 1 elif direction == 2: position.y += 1 elif direction == 3: position.x -= 1 elif direction == 4: position.y += 1 position.x -= 1 elif direction == 5: position.y += 1 position.x += 1 elif direction == 6: position.y -= 1 position.x -= 1 elif direction == 7: position.y -= 1 position.x += 1 position.z += level # We don't walk out of the map! if position.x < 1 or position.y < 1 or position.x > game.map.mapInfo.width or position.y > game.map.mapInfo.height: return False # New Tile newTile = getTile(position) if not newTile: return False # oldTile oldTile = getTile(oldPosition) val = game.scriptsystem.get("move").run(creature=self) if val == False: return False # Deal with walkOff walkOffEvent = game.scriptsystem.get('walkOff') for item in oldTile: if item.isItem(): val = walkOffEvent.run(thing=item, creature=self, position=oldPosition) if val == False: return False try: oldStackpos = oldTile.findStackpos(self) except: return False # Not on Tile. # PZ blocked? if (self.hasCondition(CONDITION_PZBLOCK) or self.getSkull() in SKULL_JUSTIFIED ) and newTile.getFlags() & TILEFLAGS_PROTECTIONZONE: self.lmessage("You are PZ blocked") return False # Block movement, even if script stop us later. self.lastStep = _time delay = self.stepDuration( newTile[0]) * (config.diagonalWalkCost if direction > 3 else 1) self.lastAction = _time + delay # Deal with walkOn walkOnEvent = game.scriptsystem.get('walkOn') for thing in newTile: if thing.solid: if level and not thing.isCreature(): continue # Monsters might push. This should probably be a preWalkOn event, but well. Consider this a todo for v1.1 or something. if push and self.isMonster() and thing.isMonster( ) and self.base._pushCreatures and thing.isPushable(self): # Push stuff here. # Clear up the creatures actions. thing.stopAction() thing.clearMove(direction, failback) if thing.move(thing.reverseDirection(), stopIfLock=True, push=False): # We "must" break here. Assume the tile is good since another creature is on it. Iterator might be invalid at this point. break elif self.base._hostile and thing.isAttackable(self): # We can attack the creature. self.target = thing self.targetMode = 1 # Deliver final blow. thing.onHit(self, -thing.data['healthmax'], PHYSICAL) #self.turn(direction) # Fix me? self.notPossible() return False if thing.isItem(): # Script. r = walkOnEvent.run(thing=thing, creature=self, position=position, fromPosition=oldPosition) if r == False: return False # Prevent monsters from moving on teleports. teledest = thing.teledest if teledest: if not isPlayer: return False try: self.teleport( Position(teledest[0], teledest[1], teledest[2]), self.position.instanceId) self.magicEffect(EFFECT_TELEPORT) except: print( "%d (%s) got a invalid teledist (%s), remove it!" % (thing.itemId, thing, teledest)) del thing.teledest newStackPos = newTile.placeCreature(self) oldTile.removeCreature(self) # Clear target if we change level if level: self.cancelTarget() self.target = None self.targetMode = 0 # Send to Player if isPlayer: # Mark for save self.saveData = True ignore = (self, ) stream = self.packet() if (oldPosition.z != 7 or position.z < 8): # Only as long as it's not 7->8 or 8->7 #stream = spectator.packet(0x6D) stream.uint8(0x6D) stream.position(oldPosition) stream.uint8(oldStackpos) stream.position(position) else: stream.removeTileItem(oldPosition, oldStackpos) # Levels if oldPosition.z > position.z: stream.moveUpPlayer(self, oldPosition) elif oldPosition.z < position.z: stream.moveDownPlayer(self, oldPosition) # Y movements if oldPosition.y > position.y: stream.uint8(0x65) stream.mapDescription( Position(oldPosition.x - 8, position.y - 6, position.z), 18, 1, self) elif oldPosition.y < position.y: stream.uint8(0x67) stream.mapDescription( Position(oldPosition.x - 8, position.y + 7, position.z), 18, 1, self) # X movements if oldPosition.x < position.x: stream.uint8(0x66) stream.mapDescription( Position(position.x + 9, position.y - 6, position.z), 1, 14, self) elif oldPosition.x > position.x: stream.uint8(0x68) stream.mapDescription( Position(position.x - 8, position.y - 6, position.z), 1, 14, self) # If we're entering protected zone, fix icons pzStatus = newTile.getFlags() & TILEFLAGS_PROTECTIONZONE pzIcon = self.extraIcons & CONDITION_PROTECTIONZONE if pzStatus and not pzIcon: self.setIcon(CONDITION_PROTECTIONZONE) self.refreshConditions(stream) if not level: self.cancelTarget(stream) self.target = None if isPlayer and self.mounted: call_later( 0, self.changeMountStatus, False ) # This should be sent after the move is completed, I believe. elif not pzStatus and pzIcon: self.removeIcon(CONDITION_PROTECTIONZONE) self.refreshConditions(stream) stream.send(self.client) else: ignore = () self.position = position self.direction = direction % 4 oldPosCreatures = getPlayers(oldPosition, ignore=ignore) newPosCreatures = getPlayers(position, ignore=ignore) spectators = oldPosCreatures | newPosCreatures invisible = self.isMonster() and self.hasCondition(CONDITION_INVISIBLE) if not invisible: for spectator in spectators: canSeeNew = spectator in newPosCreatures canSeeOld = spectator in oldPosCreatures stream = spectator.packet() if not canSeeOld and canSeeNew: stream.addTileCreature(position, newStackPos, self, spectator, resend=True) elif canSeeOld and not canSeeNew: stream.removeTileItem(oldPosition, oldStackpos) """spectator.knownCreatures.remove(self) self.knownBy.remove(spectator)""" elif canSeeOld and canSeeNew: if ( oldPosition.z != 7 or position.z < 8 ) and oldStackpos < 10: # Only as long as it's not 7->8 or 8->7 stream.uint8(0x6D) stream.position(oldPosition) stream.uint8(oldStackpos) stream.position(position) else: stream.removeTileItem(oldPosition, oldStackpos) spectator.knownCreatures.remove(self) self.knownBy.remove(spectator) stream.addTileCreature(position, newStackPos, self, spectator) stream.send(spectator.client) if self.scripts["onNextStep"]: scripts = self.scripts["onNextStep"][:] self.scripts["onNextStep"] = [] for script in scripts: script(self) # Deal with appear and disappear. Ahh the power of sets :) disappearFrom = oldPosCreatures - newPosCreatures appearTo = newPosCreatures - oldPosCreatures disappear_event = game.scriptsystem.get('disappear') for creature2 in disappearFrom: disappear_event.run(creature=creature2, creature2=self) disappear_event.run(creature=self, creature2=creature2) appear_event = game.scriptsystem.get('appear') for creature2 in appearTo: appear_event.run(creature=creature2, creature2=self) appear_event.run(creature=self, creature2=creature2) # Stairhop delay if level and isPlayer: self.lastStairHop = _time if isPlayer and self.target and not self.canSee(self.target.position): self.cancelTarget() return True
def teleport(self, position, force=False): """if not self.actionLock(self.teleport, position): return False""" # 4 steps, remove item (creature), send new map and cords, and effects oldPosition = self.position.copy() target = self.target newTile = getTile(position) oldPosCreatures = set() if not newTile: raise game.errors.SolidTile( "Tile doesn't exist") # Yea, it's fatal, even in force mode! if not force: for i in newTile.getItems(): if i.solid: raise game.errors.SolidTile() invisible = self.isMonster() and self.hasCondition(CONDITION_INVISIBLE) oldStackpos = 0 if not invisible: try: oldStackpos = getTile(oldPosition).findStackpos(self) for spectator in getSpectators(oldPosition, ignore=(self, )): stream = spectator.packet() stream.removeTileItem(oldPosition, oldStackpos) stream.magicEffect(oldPosition, 0x02) stream.send(spectator) oldPosCreatures = getCreatures(oldPosition) except: pass # Just append creature stackpos = placeCreature(self, position) if not stackpos: raise game.errors.ImpossibleMove() removeCreature(self, oldPosition) if self.creatureType == 0 and self.client: with self.packet() as stream: if oldStackpos: stream.removeTileItem(oldPosition, oldStackpos) stream.uint8(0x64) stream.position(position) stream.mapDescription( Position(position.x - 8, position.y - 6, position.z), 18, 14, self) # If we're entering protected zone, fix icons pzStatus = newTile.getFlags() & TILEFLAGS_PROTECTIONZONE pzIcon = self.extraIcons & CONDITION_PROTECTIONZONE if pzStatus and not pzIcon: self.setIcon(CONDITION_PROTECTIONZONE) self.refreshConditions(stream) self.cancelTarget(stream) self.target = None if self.isPlayer() and self.mounted: call_later(0, self.changeMountStatus, False) elif not pzStatus and pzIcon: self.removeIcon(CONDITION_PROTECTIONZONE) self.refreshConditions(stream) #stream.magicEffect(position, 0x02) self.position = position newPosCreatures = getCreatures(position) disappearFrom = oldPosCreatures - newPosCreatures appearTo = newPosCreatures - oldPosCreatures for creature2 in disappearFrom: game.scriptsystem.get('disappear').run(creature2=creature2, creature=self) for creature2 in appearTo: game.scriptsystem.get('appear').run(creature2=creature2, creature=self) if not invisible: for spectator in getSpectators(position, ignore=(self, )): stream = spectator.packet() stream.addTileCreature(position, stackpos, self, spectator.player) stream.magicEffect(position, 0x02) stream.send(spectator) if target and not self.canSee(target.position): self.cancelTarget() self.target = None self.targetMode = 0 # Stairhop delay if self.isPlayer(): self.lastStairHop = time.time() if target and not self.canSee(target.position): self.cancelTarget()
def onFirstPacket(self, packet): packetType = packet.uint8() IN_TEST = False if packetType == 0xFF: # Special case for tests. IN_TEST = True if packetType == 0x0A and not self.ready: packet.pos += 2 # OS 0x00 and 0x01 #packet.uint16() version = packet.uint16() # Version int if version >= 972: version = packet.uint32() packet.uint8() # Client type. self.protocol = game.protocol.getProtocol(version) self.version = version print "Client protocol version %d" % version if not self.protocol: log.msg("Trying to load a invalid protocol") self.transport.loseConnection() return if not IN_TEST: if (len(packet.data) - packet.pos) == 128: # RSA 1024 is always 128 packet.data = otcrypto.decryptRSA(packet.getData()) # NOTICE: Should we do it in a seperate thread? packet.pos = 0 # Reset position else: log.msg("RSA, length != 128 (it's %d)" % (packet.length - packet.pos)) self.transport.loseConnection() return if not packet.data or packet.uint8(): # RSA needs to decrypt just fine, so we get the data, and the first byte should be 0 log.msg("RSA, first char != 0") self.transport.loseConnection() return # Set the XTEA key k = (packet.uint32(), packet.uint32(), packet.uint32(), packet.uint32()) sum = 0 self.xtea = {} for x in xrange(32): self.xtea[x] = sum + k[sum & 3] & 0xffffffff sum = (sum + 0x9E3779B9) & 0xffffffff self.xtea[32 + x] = sum + k[sum>>11 & 3] & 0xffffffff ip = self.transport.getPeer().host # Ban check. if game.ban.ipIsBanned(ip): self.exitWithError("Your ip is banned.\n %s" % game.ban.banIps[ip].message()) return if config.gameMaxConnections <= (self.connections + len(waitingListIps)): if ip in waitingListIps: i = waitingListIps.index(ip) + 1 lastChecks[ip] = time.time() # Note: Everyone below this threshhold might connect. So even if your #1 on the list and there is two free slots, you can be unlucky and don't get them. if i + self.connections > config.gameMaxConnections: self.exitWaitingList("Too many players online. You are at place %d on the waiting list." % i, i) return else: waitingListIps.append(ip) lastChecks[ip] = time.time() self.exitWaitingList("Too many players online. You are at place %d on the waiting list." % len(waitingListIps), len(waitingListIps)) return self.connections += 1 try: waitingListIps.remove(ip) del lastChecks[ip] except: pass # "Gamemaster" mode? gamemaster = packet.uint8() # Check if version is correct if version > config.versionMax or version < config.versionMin: self.exitWithError(config.versionError) return # Some weird thing with 9.7. try: # Check if there is a username (and a password) username = packet.string() characterName = packet.string() password = packet.string() except: self.exitWithError("Try again.") return if (not username and not config.anyAccountWillDo) or not characterName: self.exitWithError("Could not get your account name, or character name") return packet.pos += 6 # I don't know what this is # Our funny way of doing async SQL account = yield sql.conn.runQuery("SELECT `id`,`language` FROM `accounts` WHERE `name` = %s AND `password` = SHA1(CONCAT(`salt`, %s))", (username, password)) if not account: account = game.scriptsystem.get("loginAccountFailed").runSync(None, client=self, username=username, password=password) if not account or account == True: self.exitWithError("Invalid username or password") return account = account[0] # Ban check. if game.ban.accountIsBanned(account['id']): self.exitWithError("Your account is banned.\n %s" % game.ban.banAccounts[account['id']].message()) return if not len(account) >= 2 or not account['language']: language = config.defaultLanguage else: language = account['language'] character = yield sql.conn.runQuery("SELECT p.`id`,p.`name`,p.`world_id`,p.`group_id`,p.`account_id`,p.`vocation`,p.`health`,p.`mana`,p.`soul`,p.`manaspent`,p.`experience`,p.`posx`,p.`posy`,p.`posz`,p.`instanceId`,p.`sex`,p.`looktype`,p.`lookhead`,p.`lookbody`,p.`looklegs`,p.`lookfeet`,p.`lookaddons`,p.`lookmount`,p.`town_id`,p.`skull`,p.`stamina`, p.`storage`, p.`inventory`, p.`depot`, p.`conditions`, s.`fist`,s.`fist_tries`,s.`sword`,s.`sword_tries`,s.`club`,s.`club_tries`,s.`axe`,s.`axe_tries`,s.`distance`,s.`distance_tries`,s.`shield`,s.`shield_tries`,s.`fishing`, s.`fishing_tries`, g.`guild_id`, g.`guild_rank`, p.`balance` FROM `players` AS `p` LEFT JOIN player_skills AS `s` ON p.`id` = s.`player_id` LEFT JOIN player_guild AS `g` ON p.`id` = g.`player_id` WHERE p.account_id = %s AND p.`name` = %s AND p.`world_id` = %s", (account['id'], characterName, config.worldId)) if not character: character = game.scriptsystem.get("loginCharacterFailed").runSync(None, client=self, account=account, name=characterName) if not character or character == True: self.exitWithError("Character can't be loaded") return character = character[0] if gamemaster and character['group_id'] < 3: self.exitWithError("You are not gamemaster! Turn off gamemaster mode in your IP changer.") return # Ban check. if isinstance(character, game.player.Player): if game.ban.playerIsBanned(character): self.exitWithError("Your player is banned.\n %s" % game.ban.banAccounts[character.data["id"]].message()) return elif game.ban.playerIsBanned(character['id']): self.exitWithError("Your player is banned.\n %s" % game.ban.banAccounts[character['id']].message()) return # If we "made" a new character in a script, character = the player. player = None if isinstance(character, game.player.Player): player = character game.player.allPlayers[player.name()] = player elif character['name'] in game.player.allPlayers: player = game.player.allPlayers[character['name']] if player.client: self.exitWithError("This character is already logged in!") return sql.runOperation("UPDATE `players` SET `lastlogin` = %s, `online` = 1 WHERE `id` = %s", (int(time.time()), character['id'])) if player: self.player = player if self.player.data["health"] <= 0: self.player.onSpawn() self.player.client = self tile = getTile(self.player.position) tile.placeCreature(self.player) # Send update tile to refresh all players. We use refresh because it fixes the order of things as well. updateTile(self.player.position, tile) else: # Bulld the dict since we disabled automaticly doing this. Here we cast Decimal objects to int aswell (no longer automaticly either) yield deathlist.loadDeathList(character['id']) character["language"] = language game.player.allPlayers[character['name']] = game.player.Player(self, character) self.player = game.player.allPlayers[character['name']] if self.player.data["health"] <= 0: self.player.onSpawn() try: tile = getTile(self.player.position) tile.placeCreature(self.player) # Send update tile to refresh all players. We use refresh because it fixes the order of things as well. updateTile(self.player.position, tile) except AttributeError: self.player.position = Position(*game.map.mapInfo.towns[1][1]) tile = getTile(self.player.position) tile.placeCreature(self.player) # Send update tile to refresh all players. We use refresh because it fixes the order of things as well. updateTile(self.player.position, tile) # Update last login sql.runOperation("UPDATE `players` SET `lastlogin` = %s WHERE `id` = %s", (int(time.time()), character['id'])) self.packet = self.player.packet self.player.sendFirstPacket() self.ready = True # We can now accept other packages # Call the login script game.scriptsystem.get("login").runSync(self.player) # If we got a waiting list, now is a good time to verify the list if lastChecks: checkTime = time.time() for entry in lastChecks: if checkTime - lastChecks[entry] > 3600: waitingListIps.remove(entry) del lastChecks[entry] elif packetType == 0x00 and self.transport.getPeer().host in config.executeProtocolIps: self.gotFirst = False t = TibiaPacket() if not config.executeProtocolAuthKeys: self.ready = 2 try: while True: op = packet.string() print op if op == "CALL" and self.ready == 2: print "do this" result = yield game.functions.executeCode(packet.string()) t.string(result) elif op == "AUTH": print "auth" result = packet.string() in config.executeProtocolAuthKeys if result: t.string("True") self.ready = 2 else: t.string("False") except struct.error: pass # End of the line t.send(self)
def onFirstPacket(self, packet): packetType = packet.uint8() IN_TEST = False if packetType == 0xFF: # Special case for tests. IN_TEST = True if packetType == 0x0A and not self.ready: packet.pos += 2 # OS 0x00 and 0x01 #packet.uint16() version = packet.uint16() # Version int if version >= 972: version = packet.uint32() packet.uint8() # Client type. self.protocol = game.protocol.getProtocol(version) self.version = version print "Client protocol version %d" % version if not self.protocol: log.msg("Trying to load a invalid protocol") self.transport.loseConnection() return if not IN_TEST: if (len(packet.data) - packet.pos) == 128: # RSA 1024 is always 128 packet.data = otcrypto.decryptRSA(packet.getData( )) # NOTICE: Should we do it in a seperate thread? packet.pos = 0 # Reset position else: log.msg("RSA, length != 128 (it's %d)" % (packet.length - packet.pos)) self.transport.loseConnection() return if not packet.data or packet.uint8( ): # RSA needs to decrypt just fine, so we get the data, and the first byte should be 0 log.msg("RSA, first char != 0") self.transport.loseConnection() return # Set the XTEA key k = (packet.uint32(), packet.uint32(), packet.uint32(), packet.uint32()) sum = 0 self.xtea = {} for x in xrange(32): self.xtea[x] = sum + k[sum & 3] & 0xffffffff sum = (sum + 0x9E3779B9) & 0xffffffff self.xtea[32 + x] = sum + k[sum >> 11 & 3] & 0xffffffff ip = self.transport.getPeer().host # Ban check. if game.ban.ipIsBanned(ip): self.exitWithError("Your ip is banned.\n %s" % game.ban.banIps[ip].message()) return if config.gameMaxConnections <= (self.connections + len(waitingListIps)): if ip in waitingListIps: i = waitingListIps.index(ip) + 1 lastChecks[ip] = time.time() # Note: Everyone below this threshhold might connect. So even if your #1 on the list and there is two free slots, you can be unlucky and don't get them. if i + self.connections > config.gameMaxConnections: self.exitWaitingList( "Too many players online. You are at place %d on the waiting list." % i, i) return else: waitingListIps.append(ip) lastChecks[ip] = time.time() self.exitWaitingList( "Too many players online. You are at place %d on the waiting list." % len(waitingListIps), len(waitingListIps)) return self.connections += 1 try: waitingListIps.remove(ip) del lastChecks[ip] except: pass # "Gamemaster" mode? gamemaster = packet.uint8() # Check if version is correct if version > config.versionMax or version < config.versionMin: self.exitWithError(config.versionError) return # Some weird thing with 9.7. try: # Check if there is a username (and a password) username = packet.string() characterName = packet.string() password = packet.string() except: self.exitWithError("Try again.") return if (not username and not config.anyAccountWillDo) or not characterName: self.exitWithError( "Could not get your account name, or character name") return packet.pos += 6 # I don't know what this is # Our funny way of doing async SQL account = yield sql.conn.runQuery( "SELECT `id`,`language` FROM `accounts` WHERE `name` = %s AND `password` = SHA1(CONCAT(`salt`, %s))", (username, password)) if not account: account = game.scriptsystem.get("loginAccountFailed").runSync( None, client=self, username=username, password=password) if not account or account == True: self.exitWithError("Invalid username or password") return account = account[0] # Ban check. if game.ban.accountIsBanned(account['id']): self.exitWithError( "Your account is banned.\n %s" % game.ban.banAccounts[account['id']].message()) return if not len(account) >= 2 or not account['language']: language = config.defaultLanguage else: language = account['language'] character = yield sql.conn.runQuery( "SELECT p.`id`,p.`name`,p.`world_id`,p.`group_id`,p.`account_id`,p.`vocation`,p.`health`,p.`mana`,p.`soul`,p.`manaspent`,p.`experience`,p.`posx`,p.`posy`,p.`posz`,p.`instanceId`,p.`sex`,p.`looktype`,p.`lookhead`,p.`lookbody`,p.`looklegs`,p.`lookfeet`,p.`lookaddons`,p.`lookmount`,p.`town_id`,p.`skull`,p.`stamina`, p.`storage`, p.`inventory`, p.`depot`, p.`conditions`, s.`fist`,s.`fist_tries`,s.`sword`,s.`sword_tries`,s.`club`,s.`club_tries`,s.`axe`,s.`axe_tries`,s.`distance`,s.`distance_tries`,s.`shield`,s.`shield_tries`,s.`fishing`, s.`fishing_tries`, g.`guild_id`, g.`guild_rank`, p.`balance` FROM `players` AS `p` LEFT JOIN player_skills AS `s` ON p.`id` = s.`player_id` LEFT JOIN player_guild AS `g` ON p.`id` = g.`player_id` WHERE p.account_id = %s AND p.`name` = %s AND p.`world_id` = %s", (account['id'], characterName, config.worldId)) if not character: character = game.scriptsystem.get( "loginCharacterFailed").runSync(None, client=self, account=account, name=characterName) if not character or character == True: self.exitWithError("Character can't be loaded") return character = character[0] if gamemaster and character['group_id'] < 3: self.exitWithError( "You are not gamemaster! Turn off gamemaster mode in your IP changer." ) return # Ban check. if isinstance(character, game.player.Player): if game.ban.playerIsBanned(character): self.exitWithError( "Your player is banned.\n %s" % game.ban.banAccounts[character.data["id"]].message()) return elif game.ban.playerIsBanned(character['id']): self.exitWithError( "Your player is banned.\n %s" % game.ban.banAccounts[character['id']].message()) return # If we "made" a new character in a script, character = the player. player = None if isinstance(character, game.player.Player): player = character game.player.allPlayers[player.name()] = player elif character['name'] in game.player.allPlayers: player = game.player.allPlayers[character['name']] if player.client: self.exitWithError("This character is already logged in!") return sql.runOperation( "UPDATE `players` SET `lastlogin` = %s, `online` = 1 WHERE `id` = %s", (int(time.time()), character['id'])) if player: self.player = player if self.player.data["health"] <= 0: self.player.onSpawn() self.player.client = self tile = getTile(self.player.position) tile.placeCreature(self.player) # Send update tile to refresh all players. We use refresh because it fixes the order of things as well. updateTile(self.player.position, tile) else: # Bulld the dict since we disabled automaticly doing this. Here we cast Decimal objects to int aswell (no longer automaticly either) yield deathlist.loadDeathList(character['id']) character["language"] = language game.player.allPlayers[character['name']] = game.player.Player( self, character) self.player = game.player.allPlayers[character['name']] if self.player.data["health"] <= 0: self.player.onSpawn() try: tile = getTile(self.player.position) tile.placeCreature(self.player) # Send update tile to refresh all players. We use refresh because it fixes the order of things as well. updateTile(self.player.position, tile) except AttributeError: self.player.position = Position( *game.map.mapInfo.towns[1][1]) tile = getTile(self.player.position) tile.placeCreature(self.player) # Send update tile to refresh all players. We use refresh because it fixes the order of things as well. updateTile(self.player.position, tile) # Update last login sql.runOperation( "UPDATE `players` SET `lastlogin` = %s WHERE `id` = %s", (int(time.time()), character['id'])) self.packet = self.player.packet self.player.sendFirstPacket() self.ready = True # We can now accept other packages # Call the login script game.scriptsystem.get("login").runSync(self.player) # If we got a waiting list, now is a good time to verify the list if lastChecks: checkTime = time.time() for entry in lastChecks: if checkTime - lastChecks[entry] > 3600: waitingListIps.remove(entry) del lastChecks[entry] elif packetType == 0x00 and self.transport.getPeer( ).host in config.executeProtocolIps: self.gotFirst = False t = TibiaPacket() if not config.executeProtocolAuthKeys: self.ready = 2 try: while True: op = packet.string() print op if op == "CALL" and self.ready == 2: print "do this" result = yield game.functions.executeCode( packet.string()) t.string(result) elif op == "AUTH": print "auth" result = packet.string( ) in config.executeProtocolAuthKeys if result: t.string("True") self.ready = 2 else: t.string("False") except struct.error: pass # End of the line t.send(self)
def move(self, direction, spectators=None, level=0, stopIfLock=False, callback=None, failback=None, push=True): if not self.alive or not self.actionLock(self.move, direction, spectators, level, stopIfLock, callback, failback, push): return if not self.data["health"] or not self.canMove or not self.speed: return False oldPosition = self.position.copy() # Drunk? if self.hasCondition(CONDITION_DRUNK): directions = [0,1,2,3,4,5,6,7,direction] # Double the odds of getting the correct direction. directions.remove(self.reverseDirection()) # From a reality perspective, you are rarely that drunk. direction = random.choice([0,1,2,3,4,5,6,7,direction]) # Recalculate position position = oldPosition.copy() if direction == 0: position.y -= 1 elif direction == 1: position.x += 1 elif direction == 2: position.y += 1 elif direction == 3: position.x -= 1 elif direction == 4: position.y += 1 position.x -= 1 elif direction == 5: position.y += 1 position.x += 1 elif direction == 6: position.y -= 1 position.x -= 1 elif direction == 7: position.y -= 1 position.x += 1 position.z += level # We don't walk out of the map! if position.x < 1 or position.y < 1 or position.x > game.map.mapInfo.width or position.y > game.map.mapInfo.height: self.cancelWalk() return # New Tile newTile = getTile(position) if not newTile: return self.clearMove(direction, failback) # oldTile oldTile = getTile(oldPosition) if not oldTile: # This always raise raise Exception("(old)Tile not found (%s). This shouldn't happend!" % oldPosition) val = game.scriptsystem.get("move").runSync(self) if val == False: return self.clearMove(direction, failback) try: oldStackpos = oldTile.findStackpos(self) except: return self.clearMove(direction, failback) # Deal with walkOff for item in oldTile.getItems(): game.scriptsystem.get('walkOff').runSync(item, self, None, position=oldPosition) # Deal with preWalkOn for item in newTile.getItems(): r = game.scriptsystem.get('preWalkOn').runSync(item, self, None, oldTile=oldTile, newTile=newTile, position=position) if r == False: return self.clearMove(direction, failback) # PZ blocked? if (self.hasCondition(CONDITION_PZBLOCK) or self.getSkull() in SKULL_JUSTIFIED) and newTile.getFlags() & TILEFLAGS_PROTECTIONZONE: self.lmessage("You are PZ blocked") return self.clearMove(direction, failback) if newTile.ground.solid: self.notPossible() return self.clearMove(direction, failback) if newTile.things: for thing in newTile.things: if not self.isPlayer() and isinstance(thing, Item) and thing.teleport: return self.clearMove(direction, failback) if thing.solid: if level and isinstance(thing, Creature): continue # Monsters might push. This should probably be a preWalkOn event, but well. Consider this a todo for v1.1 or something. if push and isinstance(thing, Monster) and self.isMonster() and self.base.pushCreatures and thing.base.pushable: # Push stuff here. # Clear up the creatures actions. thing.stopAction() thing.clearMove(direction, failback) if thing.move(thing.reverseDirection(), stopIfLock=True, push=False): # We "must" break here. Assume the tile is good since another creature is on it. Iterator might be invalid at this point. break elif self.base.hostile and thing.isAttackable(self): # We can attack the creature. self.target = thing self.targetMode = 1 # Deliver final blow. thing.onHit(self, -thing.data['healthmax'], PHYSICAL) #self.turn(direction) # Fix me? self.notPossible() return self.clearMove(direction, failback) _time = time.time() self.lastStep = _time delay = self.stepDuration(newTile.ground) * (config.diagonalWalkCost if direction > 3 else 1) self.lastAction = _time + delay newStackPos = newTile.placeCreature(self) oldTile.removeCreature(self) # Weird #112 issue. Hack fix. if self in oldTile.things: while True: if self not in oldTile.things: break oldTile.removeCreature(self) # Clear target if we change level if level: self.cancelTarget() self.target = None self.targetMode = 0 # Send to Player if self.isPlayer(): # Mark for save self.saveData = True ignore = (self,) stream = self.packet() if (oldPosition.z != 7 or position.z < 8): # Only as long as it's not 7->8 or 8->7 #stream = spectator.packet(0x6D) stream.uint8(0x6D) stream.position(oldPosition) stream.uint8(oldStackpos) stream.position(position) else: stream.removeTileItem(oldPosition, oldStackpos) # Levels if oldPosition.z > position.z: stream.moveUpPlayer(self, oldPosition) elif oldPosition.z < position.z: stream.moveDownPlayer(self, oldPosition) # Y movements if oldPosition.y > position.y: stream.uint8(0x65) stream.mapDescription(Position(oldPosition.x - 8, position.y - 6, position.z), 18, 1, self) elif oldPosition.y < position.y: stream.uint8(0x67) stream.mapDescription(Position(oldPosition.x - 8, position.y + 7, position.z), 18, 1, self) # X movements if oldPosition.x < position.x: stream.uint8(0x66) stream.mapDescription(Position(position.x + 9, position.y - 6, position.z), 1, 14, self) elif oldPosition.x > position.x: stream.uint8(0x68) stream.mapDescription(Position(position.x - 8, position.y - 6, position.z), 1, 14, self) # If we're entering protected zone, fix icons pzStatus = newTile.getFlags() & TILEFLAGS_PROTECTIONZONE pzIcon = self.extraIcons & CONDITION_PROTECTIONZONE if pzStatus and not pzIcon: self.setIcon(CONDITION_PROTECTIONZONE) self.refreshConditions(stream) if not level: self.cancelTarget(stream) self.target = None if self.isPlayer() and self.mounted: callLater(0, self.changeMountStatus, False) # This should be sent after the move is completed, I believe. elif not pzStatus and pzIcon: self.removeIcon(CONDITION_PROTECTIONZONE) self.refreshConditions(stream) stream.send(self.client) else: ignore = () self.position = position self.direction = direction % 4 oldPosCreatures = getPlayers(oldPosition, ignore=ignore) newPosCreatures = getPlayers(position, ignore=ignore) spectators = oldPosCreatures|newPosCreatures invisible = self.isMonster() and self.hasCondition(CONDITION_INVISIBLE) for spectator in spectators: # Make packet if not spectator.client or invisible: continue canSeeNew = spectator in newPosCreatures if canSeeNew: assert spectator.canSee(position) canSeeOld = spectator in oldPosCreatures if canSeeOld: assert spectator.canSee(oldPosition) stream = spectator.packet() if not canSeeOld and canSeeNew: stream.addTileCreature(position, newStackPos, self, spectator, resend=True) elif canSeeOld and not canSeeNew: stream.removeTileItem(oldPosition, oldStackpos) """spectator.knownCreatures.remove(self) self.knownBy.remove(spectator)""" elif canSeeOld and canSeeNew: if (oldPosition.z != 7 or position.z < 8) and oldStackpos < 10: # Only as long as it's not 7->8 or 8->7 stream.uint8(0x6D) stream.position(oldPosition) stream.uint8(oldStackpos) stream.position(position) else: stream.removeTileItem(oldPosition, oldStackpos) spectator.knownCreatures.remove(self) self.knownBy.remove(spectator) stream.addTileCreature(position, newStackPos, self, spectator) stream.send(spectator.client) if self.scripts["onNextStep"]: scripts = self.scripts["onNextStep"][:] self.scripts["onNextStep"] = [] for script in scripts: script(self) # Deal with walkOn for item in newTile.getItems(): # Scripts game.scriptsystem.get('walkOn').runDeferNoReturn(item, self, None, position=position, fromPosition=oldPosition) if item.teledest: try: self.teleport(Position(item.teledest[0], item.teledest[1], item.teledest[2]), self.position.instanceId) self.magicEffect(EFFECT_TELEPORT) except: log.msg("%d (%s) got a invalid teledist (%s), remove it!" % (item.itemId, item, item.teledest)) del item.teledest # Deal with appear and disappear. Ahh the power of sets :) disappearFrom = oldPosCreatures - newPosCreatures appearTo = newPosCreatures - oldPosCreatures for creature2 in disappearFrom: game.scriptsystem.get('disappear').runDeferNoReturn(creature2, self) game.scriptsystem.get('disappear').runDeferNoReturn(self, creature2) for creature2 in appearTo: game.scriptsystem.get('appear').runDeferNoReturn(creature2, self) game.scriptsystem.get('appear').runDeferNoReturn(self, creature2) # Stairhop delay if level and self.isPlayer(): self.lastStairHop = time.time() if self.isPlayer() and self.target and not self.canSee(self.target.position): self.cancelTarget() if callback: reactor.callLater(0, callback) return True
def teleport(self, position, force=False): """if not self.actionLock(self.teleport, position): return False""" # 4 steps, remove item (creature), send new map and cords, and effects oldPosition = self.position.copy() newTile = getTile(position) oldPosCreatures = set() if not newTile: raise game.errors.SolidTile("Tile doesn't exist") # Yea, it's fatal, even in force mode! if not force: for i in newTile.getItems(): if i.solid: raise game.errors.SolidTile() invisible = self.isMonster() and self.hasCondition(CONDITION_INVISIBLE) oldStackpos = 0 if not invisible: try: oldStackpos = getTile(oldPosition).findStackpos(self) for spectator in getSpectators(oldPosition, ignore=(self,)): stream = spectator.packet() stream.removeTileItem(oldPosition, oldStackpos) stream.magicEffect(oldPosition, 0x02) stream.send(spectator) oldPosCreatures = getCreatures(oldPosition) except: pass # Just append creature stackpos = placeCreature(self, position) if not stackpos: raise game.errors.ImpossibleMove() removeCreature(self, oldPosition) if self.creatureType == 0 and self.client: print "Good" with self.packet() as stream: if oldStackpos: stream.removeTileItem(oldPosition, oldStackpos) stream.uint8(0x64) stream.position(position) stream.mapDescription(Position(position.x - 8, position.y - 6, position.z), 18, 14, self) # If we're entering protected zone, fix icons pzStatus = newTile.getFlags() & TILEFLAGS_PROTECTIONZONE pzIcon = self.extraIcons & CONDITION_PROTECTIONZONE if pzStatus and not pzIcon: self.setIcon(CONDITION_PROTECTIONZONE) self.refreshConditions(stream) self.cancelTarget(stream) self.target = None if self.isPlayer() and self.mounted: callLater(0, self.changeMountStatus, False) elif not pzStatus and pzIcon: self.removeIcon(CONDITION_PROTECTIONZONE) self.refreshConditions(stream) #stream.magicEffect(position, 0x02) self.position = position newPosCreatures = getCreatures(position) disappearFrom = oldPosCreatures - newPosCreatures appearTo = newPosCreatures - oldPosCreatures for creature2 in disappearFrom: game.scriptsystem.get('disappear').runSync(creature2, self) for creature2 in appearTo: game.scriptsystem.get('appear').runSync(creature2, self) if not invisible: for spectator in getSpectators(position, ignore=(self,)): stream = spectator.packet() stream.addTileCreature(position, stackpos, self, spectator.player) stream.magicEffect(position, 0x02) stream.send(spectator) if self.target and not self.canSee(self.target.position): self.cancelTarget() self.target = None self.targetMode = 0 # Stairhop delay if self.isPlayer(): self.lastStairHop = time.time() if self.target and not self.canSee(self.target.position): self.cancelTarget()