def send_attack_state_update(self, damage_info): data = pack( '<I2QIBIf7I', damage_info.hit_info, damage_info.attacker.guid, damage_info.target.guid, damage_info.total_damage, # Sub damage count 1, damage_info.damage_school_mask, damage_info.total_damage, damage_info.damage, damage_info.absorb, damage_info.target_state, damage_info.resist, 0, 0, damage_info.blocked_amount) GridManager.send_surrounding( PacketWriter.get_packet(OpCode.SMSG_ATTACKERSTATEUPDATE, data), self, include_self=self.get_type() == ObjectTypes.TYPE_PLAYER) # Damage effects self.deal_damage(damage_info.target, damage_info.total_damage)
def send_update_surrounding(self, update_type=UpdateTypes.UPDATE_FULL): update_packet = UpdatePacketFactory.compress_if_needed( PacketWriter.get_packet( OpCode.SMSG_UPDATE_OBJECT, self.get_update_packet(update_type=update_type, is_self=False))) GridManager.send_surrounding(update_packet, self, include_self=False)
def send_melee_attack_stop(self, victim_guid): # Last uint32 is "deceased"; can be either 1 (self is dead), or 0, (self is alive). # Forces the unit to face the corpse and disables clientside # turning (UnitFlags.DisableMovement) CGUnit_C::OnAttackStop data = pack('<2QI', self.guid, victim_guid, 0 if self.is_alive else 1) GridManager.send_surrounding( PacketWriter.get_packet(OpCode.SMSG_ATTACKSTOP, data), self)
def send_spell_go(self, casting_spell): data = [self.unit_mgr.guid, self.unit_mgr.guid, casting_spell.spell_entry.ID, 0] # TODO Flags sign = '<2QIH' hit_count = 0 if len(casting_spell.target_results.keys()) > 0: hit_count += 1 sign += 'B' data.append(hit_count) for target, reason in casting_spell.target_results.items(): if reason == SpellMissReason.MISS_REASON_NONE: data.append(target) sign += 'Q' data.append(0) # miss count sign += 'B' sign += 'H' # SpellTargetMask data.append(casting_spell.spell_target_mask) # write initial target if casting_spell.spell_target_mask & SpellTargetMask.UNIT == SpellTargetMask.UNIT: sign += 'Q' data.append(casting_spell.initial_target_unit.guid) packed = pack(sign, *data) GridManager.send_surrounding(PacketWriter.get_packet(OpCode.SMSG_SPELL_GO, packed), self.unit_mgr, include_self=self.unit_mgr.get_type() == ObjectTypes.TYPE_PLAYER)
def deal_damage(self, target, damage): if not target or damage < 1: return if not self.in_combat: self.enter_combat(force_update=True) if not target.in_combat: target.enter_combat(force_update=True) if not target.in_combat: target.enter_combat() new_health = target.health - damage if new_health <= 0: target.die(self) else: target.set_health(new_health) update_packet = target.generate_proper_update_packet( is_self=target.get_type() == ObjectTypes.TYPE_PLAYER) GridManager.send_surrounding( update_packet, target, include_self=target.get_type() == ObjectTypes.TYPE_PLAYER)
def update_surrounding(self, destroy=False): if destroy: grid = GRIDS[self.current_grid] for guid, player in list(grid.players.items()): if player.guid != self.guid: self.session.request.sendall(player.get_destroy_packet()) update_packet = UpdatePacketFactory.compress_if_needed( PacketWriter.get_packet( OpCode.SMSG_UPDATE_OBJECT, self.get_update_packet(update_type=UpdateTypes.UPDATE_FULL, is_self=False))) GridManager.send_surrounding(update_packet, self, include_self=False) GridManager.send_surrounding(NameQueryHandler.get_query_details( self.player), self, include_self=True) for guid, player in list( GridManager.get_surrounding_players(self).items()): if self.guid != guid: self.session.request.sendall( PacketWriter.get_packet( OpCode.SMSG_UPDATE_OBJECT, player.get_update_packet( update_type=UpdateTypes.UPDATE_FULL, is_self=False))) self.session.request.sendall( NameQueryHandler.get_query_details(player.player))
def update(self): now = time.time() if now > self.last_tick > 0: elapsed = now - self.last_tick self.player.totaltime += elapsed self.player.leveltime += elapsed self.last_tick = now if self.flagged_for_update: self.session.request.sendall( PacketWriter.get_packet( OpCode.SMSG_UPDATE_OBJECT, self.get_update_packet(update_type=UpdateTypes.UPDATE_FULL, is_self=True))) GridManager.send_surrounding(PacketWriter.get_packet( OpCode.SMSG_UPDATE_OBJECT, self.get_update_packet(update_type=UpdateTypes.UPDATE_FULL, is_self=False)), self, include_self=False) GridManager.update_object(self) self.flagged_for_update = False
def update_surrounding(self): self.send_update_surrounding() GridManager.send_surrounding(NameQueryHandler.get_query_details( self.player), self, include_self=True) players, creatures, gobjects = GridManager.get_surrounding_objects( self, [ ObjectTypes.TYPE_PLAYER, ObjectTypes.TYPE_UNIT, ObjectTypes.TYPE_GAMEOBJECT ]) for guid, player in players.items(): if self.guid != guid: if guid not in self.objects_in_range: update_packet = UpdatePacketFactory.compress_if_needed( PacketWriter.get_packet( OpCode.SMSG_UPDATE_OBJECT, player.get_update_packet( update_type=UpdateTypes.UPDATE_FULL, is_self=False))) self.session.request.sendall(update_packet) self.session.request.sendall( NameQueryHandler.get_query_details(player.player)) self.objects_in_range[guid] = {'object': player, 'near': True} for guid, creature in creatures.items(): if guid not in self.objects_in_range: update_packet = UpdatePacketFactory.compress_if_needed( PacketWriter.get_packet( OpCode.SMSG_UPDATE_OBJECT, creature.get_update_packet( update_type=UpdateTypes.UPDATE_FULL, is_self=False))) self.session.request.sendall(update_packet) self.session.request.sendall(creature.query_details()) self.objects_in_range[guid] = {'object': creature, 'near': True} for guid, gobject in gobjects.items(): if guid not in self.objects_in_range: update_packet = UpdatePacketFactory.compress_if_needed( PacketWriter.get_packet( OpCode.SMSG_UPDATE_OBJECT, gobject.get_update_packet( update_type=UpdateTypes.UPDATE_FULL, is_self=False))) self.session.request.sendall(update_packet) self.session.request.sendall(gobject.query_details()) self.objects_in_range[guid] = {'object': gobject, 'near': True} for guid, object_info in list(self.objects_in_range.items()): if not object_info['near']: self.session.request.sendall( self.objects_in_range[guid]['object'].get_destroy_packet()) del self.objects_in_range[guid] else: self.objects_in_range[guid]['near'] = False
def send_move_to(self, waypoints, speed, spline_flag): self.reset() self.speed = speed start_time = int(WorldManager.get_seconds_since_startup() * 1000) data = pack('<Q12sIBI', self.unit.guid, self.unit.location.to_bytes(include_orientation=False), start_time, 0, spline_flag) waypoints_data = b'' waypoints_length = len(waypoints) last_waypoint = self.unit.location total_distance = 0 total_time = 0 current_id = 0 for waypoint in waypoints: waypoints_data += waypoint.to_bytes(include_orientation=False) current_distance = last_waypoint.distance(waypoint) current_time = current_distance / speed total_distance += current_distance total_time += current_time self.pending_waypoints.append( PendingWaypoint(current_id, total_time, waypoint)) last_waypoint = waypoint current_id += 1 data += pack('<2I%us' % len(waypoints_data), int(total_time * 1000), waypoints_length, waypoints_data) GridManager.send_surrounding(PacketWriter.get_packet( OpCode.SMSG_MONSTER_MOVE, data), self.unit, include_self=self.is_player) # Player should dismount after some seconds have passed since FP destination is reached (Blizzlike). # This is also kind of a hackfix (at least for now) since the client always takes a bit more time to reach # the actual destination than the time you specify in SMSG_MONSTER_MOVE. if self.is_player and spline_flag == SplineFlags.SPLINEFLAG_FLYING: self.total_waypoint_time = total_time + (0.15 * waypoints_length) else: self.total_waypoint_time = total_time # Generate the spline spline = MovementSpline() spline.flags = spline_flag spline.spot = self.unit.location spline.guid = self.unit.guid spline.facing = self.unit.location.o spline.elapsed = 0 spline.total_time = int(self.total_waypoint_time * 1000) spline.points = waypoints self.unit.movement_spline = spline self.last_position = self.unit.location self.should_update_waypoints = True
def send_single_item_update(self, world_session, item, is_self): update_packet = UpdatePacketFactory.compress_if_needed(PacketWriter.get_packet( OpCode.SMSG_UPDATE_OBJECT, item.get_full_update_packet(is_self=False))) if is_self: world_session.request.sendall(update_packet) world_session.request.sendall(item.query_details()) else: GridManager.send_surrounding(update_packet, world_session.player_mgr, include_self=False) GridManager.send_surrounding(item.query_details(), world_session.player_mgr, include_self=False)
def handle(world_session, socket, reader): if len(reader.data) >= 4: # Avoid handling empty player macro packet category = unpack('<I', reader.data[:4])[0] if 0x0 <= category <= 0xD: voice_packet = PacketWriter.get_packet(OpCode.SMSG_PLAYER_MACRO, pack('<QI', world_session.player_mgr.guid, category)) GridManager.send_surrounding(voice_packet, world_session.player_mgr, include_self=True) return 0
def send_move_to(self, waypoints, speed, spline_flag): self.reset() self.speed = speed start_time = int(WorldManager.get_seconds_since_startup() * 1000) location_bytes = self.unit.location.to_bytes(include_orientation=False) data = pack(f'<Q{len(location_bytes)}sIBI', self.unit.guid, location_bytes, start_time, 0, spline_flag) waypoints_data = b'' waypoints_length = len(waypoints) last_waypoint = self.unit.location total_distance = 0 total_time = 0 current_id = 0 for waypoint in waypoints: waypoints_data += waypoint.to_bytes(include_orientation=False) current_distance = last_waypoint.distance(waypoint) current_time = current_distance / speed total_distance += current_distance total_time += current_time self.pending_waypoints.append( PendingWaypoint(current_id, total_time, waypoint)) last_waypoint = waypoint current_id += 1 data += pack(f'<2I{len(waypoints_data)}s', int(total_time * 1000), waypoints_length, waypoints_data) GridManager.send_surrounding(PacketWriter.get_packet( OpCode.SMSG_MONSTER_MOVE, data), self.unit, include_self=self.is_player) # Player shouldn't instantly dismount after reaching the taxi destination if self.is_player and spline_flag == SplineFlags.SPLINEFLAG_FLYING: self.total_waypoint_time = total_time + 1.0 # Add 1 extra second else: self.total_waypoint_time = total_time # Generate the spline spline = MovementSpline() spline.flags = spline_flag spline.spot = self.unit.location spline.guid = self.unit.guid spline.facing = self.unit.location.o spline.elapsed = 0 spline.total_time = int(self.total_waypoint_time * 1000) spline.points = waypoints self.unit.movement_spline = spline self.last_position = self.unit.location self.should_update_waypoints = True
def handle(world_session, socket, reader): # TODO Not working, wrong packet data, or animation not implemented client side? player_guid = pack('<Q', world_session.player_mgr.guid) mount_anim_packet = PacketWriter.get_packet( OpCode.SMSG_MOUNTSPECIAL_ANIM, player_guid) GridManager.send_surrounding(PacketWriter.get_packet( OpCode.SMSG_TEXT_EMOTE, mount_anim_packet), world_session.player_mgr, include_self=True) return 0
def deal_spell_damage(self, target, damage, school, spell_id): # TODO Spell hit damage visual? data = pack('<IQQIIfiii', 1, self.guid, target.guid, spell_id, damage, damage, school, damage, 0) packet = PacketWriter.get_packet( OpCode.SMSG_ATTACKERSTATEUPDATEDEBUGINFOSPELL, data) GridManager.send_surrounding( packet, target, include_self=target.get_type() == ObjectTypes.TYPE_PLAYER) self.deal_damage(target, damage)
def change_turn_speed(self, turn_speed=0): if turn_speed <= 0: turn_speed = config.Unit.Player.Defaults.turn_speed self.turn_rate = turn_speed data = pack('<f', turn_speed) # TODO NOT WORKING self.session.request.sendall( PacketWriter.get_packet(OpCode.MSG_MOVE_SET_TURN_RATE_CHEAT, data)) GridManager.send_surrounding( PacketWriter.get_packet(OpCode.SMSG_UPDATE_OBJECT, self.get_movement_update_packet()), self)
def change_walk_speed(self, walk_speed=0): if walk_speed <= 0: walk_speed = config.Unit.Defaults.walk_speed elif walk_speed >= 56: walk_speed = 56 # Max speed without glitches self.walk_speed = walk_speed data = pack('<f', walk_speed) self.session.request.sendall( PacketWriter.get_packet(OpCode.MSG_MOVE_SET_WALK_SPEED, data)) GridManager.send_surrounding( PacketWriter.get_packet(OpCode.SMSG_UPDATE_OBJECT, self.get_movement_update_packet()), self)
def change_speed(self, speed=0): if speed <= 0: speed = config.Unit.Defaults.run_speed elif speed >= 56: speed = 56 # Max speed without glitches self.running_speed = speed data = pack('<f', speed) self.session.request.sendall( PacketWriter.get_packet(OpCode.SMSG_FORCE_SPEED_CHANGE, data)) GridManager.send_surrounding( PacketWriter.get_packet(OpCode.SMSG_UPDATE_OBJECT, self.get_movement_update_packet()), self)
def receive_damage(self, amount, source=None): is_player = self.get_type() == ObjectTypes.TYPE_PLAYER new_health = self.health - amount if new_health <= 0: self.die(killer=source) else: self.set_health(new_health) update_packet = self.generate_proper_update_packet(is_self=is_player) GridManager.send_surrounding(update_packet, self, include_self=is_player)
def change_swim_speed(self, swim_speed=0): if swim_speed <= 0: swim_speed = config.Unit.Defaults.swim_speed elif swim_speed >= 56: swim_speed = 56 # Max possible swim speed self.swim_speed = swim_speed data = pack('<f', swim_speed) self.session.request.sendall( PacketWriter.get_packet(OpCode.SMSG_FORCE_SWIM_SPEED_CHANGE, data)) GridManager.send_surrounding( PacketWriter.get_packet(OpCode.SMSG_UPDATE_OBJECT, self.get_movement_update_packet()), self)
def send_cast_start(self, casting_spell): data = [self.unit_mgr.guid, self.unit_mgr.guid, casting_spell.spell_entry.ID, 0, casting_spell.get_base_cast_time(), casting_spell.spell_target_mask] signature = '<2QIHiH' # TODO if casting_spell.initial_target_unit and casting_spell.spell_target_mask != SpellTargetMask.SELF: # Some self-cast spells crash client if target is written data.append(casting_spell.initial_target_unit.guid) signature += 'Q' data = pack(signature, *data) GridManager.send_surrounding(PacketWriter.get_packet(OpCode.SMSG_SPELL_START, data), self.unit_mgr, include_self=self.unit_mgr.get_type() == ObjectTypes.TYPE_PLAYER)
def teleport(self, map_, location): if not DbcDatabaseManager.map_get_by_id(map_): return False self.is_teleporting = True GridManager.send_surrounding(self.get_destroy_packet(), self, include_self=False) # Same map and not inside instance if self.map_ == map_ and self.map_ <= 1: data = pack( '<Q9fI', self.transport_id, self.transport.x, self.transport.y, self.transport.z, self.transport.o, location.x, location.y, location.z, location.o, 0, # ? 0 # MovementFlags ) self.session.request.sendall(PacketWriter.get_packet(OpCode.SMSG_MOVE_WORLDPORT_ACK, data)) # Loading screen else: data = pack('<I', map_) self.session.request.sendall(PacketWriter.get_packet(OpCode.SMSG_TRANSFER_PENDING, data)) data = pack( '<B4f', map_, location.x, location.y, location.z, location.o ) self.session.request.sendall(PacketWriter.get_packet(OpCode.SMSG_NEW_WORLD, data)) self.map_ = map_ self.location.x = location.x self.location.y = location.y self.location.z = location.z self.location.o = location.o return True
def send_update_surrounding(self, update_packet, include_self=False, create=False): if not create: self.inventory.send_inventory_update(self.session, is_self=False) self.inventory.build_update() GridManager.send_surrounding(update_packet, self, include_self=include_self) if create: GridManager.send_surrounding(NameQueryHandler.get_query_details( self.player), self, include_self=True)
def respawn(self, force_update=True): super().respawn() self.set_health(self.max_health) self.set_mana(self.max_power_1) self.is_spawned = True self.respawn_timer = 0 self.respawn_time = randint(self.creature_instance.spawntimesecsmin, self.creature_instance.spawntimesecsmax) if force_update: GridManager.send_surrounding( self.generate_proper_update_packet(create=True), self, include_self=False)
def handle_movement_status(world_session, socket, reader): movement_fmt = '<QfffffffffI' if not world_session.player_mgr.is_teleporting and len( reader.data ) >= 48: # Avoid handling malformed movement packets try: transport_guid, transport_x, transport_y, transport_z, transport_o, x, y, z, o, pitch, flags = \ unpack(movement_fmt, reader.data[:48]) world_session.player_mgr.transport_id = transport_guid world_session.player_mgr.transport_x = transport_x world_session.player_mgr.transport_y = transport_y world_session.player_mgr.transport_z = transport_z world_session.player_mgr.transport_orientation = transport_o world_session.player_mgr.location.x = x world_session.player_mgr.location.y = y world_session.player_mgr.location.z = z world_session.player_mgr.location.o = o world_session.player_mgr.pitch = pitch world_session.player_mgr.movement_flags = flags movement_data = pack('<Q%us' % len(reader.data), world_session.player_mgr.guid, reader.data) GridManager.send_surrounding(PacketWriter.get_packet( OpCode(reader.opcode), movement_data), world_session.player_mgr, include_self=False) GridManager.update_object(world_session.player_mgr) world_session.player_mgr.sync_player() if reader.opcode == OpCode.MSG_MOVE_JUMP and \ world_session.player_mgr.stand_state != StandState.UNIT_DEAD and \ world_session.player_mgr.stand_state != StandState.UNIT_STANDING: world_session.player_mgr.stand_state = StandState.UNIT_STANDING world_session.player_mgr.flagged_for_update = True except (AttributeError, error): Logger.error('Error while handling %s, skipping. Data: %s' % (OpCode(reader.opcode), reader.data)) return 0
def update(self): now = time.time() if now > self.last_tick > 0: elapsed = now - self.last_tick if self.is_alive: # Spell/aura updates self.spell_manager.update(now) self.aura_manager.update(elapsed) # Movement Updates self.movement_manager.update_pending_waypoints(elapsed) # Random Movement if not self.in_combat and self.creature_instance.movement_type == MovementTypes.WANDER: if len(self.movement_manager.pending_waypoints) == 0: if now > self.last_random_movement + self.random_movement_wait_time: self.movement_manager.move_random( self.spawn_position, self.creature_instance.wander_distance) self.random_movement_wait_time = randint(1, 12) self.last_random_movement = now # Dead else: self.respawn_timer += elapsed if self.respawn_timer >= self.respawn_time: self.respawn() # Destroy body when creature is about to respawn elif self.is_spawned and self.respawn_timer >= self.respawn_time * 0.8: self.is_spawned = False GridManager.send_surrounding(self.get_destroy_packet(), self, include_self=False) self.last_tick = now if self.dirty: GridManager.send_surrounding( self.generate_proper_update_packet(create=False), self, include_self=False) GridManager.update_object(self) self.reset_fields() self.set_dirty(is_dirty=False)
def respawn(self): super().respawn() self.set_health(self.max_health) self.set_mana(self.max_power_1) self.loot_manager.clear() self.set_lootable(False) if self.killed_by and self.killed_by.group_manager: self.killed_by.group_manager.clear_looters_for_victim(self) self.killed_by = None self.is_spawned = True self.respawn_timer = 0 self.respawn_time = randint(self.creature_instance.spawntimesecsmin, self.creature_instance.spawntimesecsmax) GridManager.send_surrounding( self.generate_proper_update_packet(create=True), self, include_self=False)
def loot_item(self, slot): if self.current_selection > 0: enemy = GridManager.get_surrounding_unit_by_guid( self, self.current_selection, include_players=False) if enemy and enemy.loot_manager.has_loot(): loot = enemy.loot_manager.get_loot_in_slot(slot) if not loot or not loot.item: self.send_loot_release(enemy.guid) return if self.inventory.add_item( item_template=loot.item.item_template, count=loot.quantity, looted=True): enemy.loot_manager.do_loot(slot) data = pack('<B', slot) GridManager.send_surrounding( PacketWriter.get_packet(OpCode.SMSG_LOOT_REMOVED, data), self) if enemy and not enemy.loot_manager.has_loot(): enemy.set_lootable(False)
def end_duel(self, duel_winner_flag, duel_complete_flag, winner): if not self.arbiter or self.duel_state == DuelState.DUEL_STATE_FINISHED or not self.players: return # Set this first to prevent next tick to trigger. self.duel_state = DuelState.DUEL_STATE_FINISHED if duel_winner_flag == DuelWinner.DUEL_WINNER_KNOCKOUT: # TODO: Should trigger EMOTE BEG on loser? # TODO: Should root loser for 3 secs? pass # Send either the duel ended by natural means or if it was canceled/interrupted packet = PacketWriter.get_packet(OpCode.SMSG_DUEL_COMPLETE, pack('<B', duel_complete_flag)) GridManager.send_surrounding(packet, self.arbiter) # Was not interrupted, broadcast duel result. if duel_complete_flag == DuelComplete.DUEL_FINISHED: winner_name_bytes = PacketWriter.string_to_bytes(winner.player.name) loser_name_bytes = PacketWriter.string_to_bytes(self.players[winner.guid].target.player.name) data = pack(f'<B{len(winner_name_bytes)}s{len(loser_name_bytes)}s', duel_winner_flag, winner_name_bytes, loser_name_bytes) packet = PacketWriter.get_packet(OpCode.SMSG_DUEL_WINNER, data) GridManager.send_surrounding(packet, self.arbiter) packet = PacketWriter.get_packet(OpCode.SMSG_CANCEL_COMBAT) for entry in self.players.values(): entry.player.session.enqueue_packet(packet) entry.player.leave_combat() self.build_update(entry.player) entry.player.set_dirty() # Clean up arbiter go and cleanup. GridManager.remove_object(self.arbiter) # Finally, flush this DualManager instance. self.flush()
def send_melee_attack_start(self, victim_guid): data = pack('<2Q', self.guid, victim_guid) GridManager.send_surrounding( PacketWriter.get_packet(OpCode.SMSG_ATTACKSTART, data), self)
def handle_movement_status(world_session, socket, reader): # Avoid handling malformed movement packets, or handling them while no player or player teleporting if world_session.player_mgr and len(reader.data) >= 48: try: transport_guid, transport_x, transport_y, transport_z, transport_o, x, y, z, o, pitch, flags = \ unpack('<Q9fI', reader.data[:48]) # Hacky way to prevent random teleports when colliding with elevators # Also acts as a rudimentary teleport cheat detection if not world_session.player_mgr.pending_taxi_destination and world_session.player_mgr.location.distance( x=x, y=y, z=z) > 64: Logger.anticheat( f'Preventing coordinate desync from player {world_session.player_mgr.player.name} ({world_session.player_mgr.guid}).' ) world_session.player_mgr.teleport( world_session.player_mgr.map_, world_session.player_mgr.location) return 0 # Send movement info to SpellManager until movement handling is merged to update system if flags & 0xF != 0: # MoveFlags.MOVEFLAG_MOVE_MASK | MoveFlags.MOVEFLAG_STRAFE_MASK world_session.player_mgr.spell_manager.flag_as_moved() world_session.player_mgr.transport_id = transport_guid world_session.player_mgr.transport.x = transport_x world_session.player_mgr.transport.y = transport_y world_session.player_mgr.transport.z = transport_z world_session.player_mgr.transport.o = transport_o world_session.player_mgr.location.x = x world_session.player_mgr.location.y = y world_session.player_mgr.location.z = z world_session.player_mgr.location.o = o world_session.player_mgr.pitch = pitch world_session.player_mgr.movement_flags = flags if flags & MoveFlags.MOVEFLAG_SPLINE_MOVER: world_session.player_mgr.movement_spline = MovementManager.MovementSpline.from_bytes( reader.data[48:]) movement_data = pack(f'<Q{len(reader.data)}s', world_session.player_mgr.guid, reader.data) GridManager.send_surrounding(PacketWriter.get_packet( OpCode(reader.opcode), movement_data), world_session.player_mgr, include_self=False) GridManager.update_object(world_session.player_mgr) world_session.player_mgr.sync_player() # Get up if you jump while not standing if reader.opcode == OpCode.MSG_MOVE_JUMP and \ world_session.player_mgr.stand_state != StandState.UNIT_DEAD and \ world_session.player_mgr.stand_state != StandState.UNIT_STANDING: world_session.player_mgr.stand_state = StandState.UNIT_STANDING world_session.player_mgr.set_dirty() except (AttributeError, error): Logger.error( f'Error while handling {OpCode(reader.opcode).name}, skipping. Data: {reader.data}' ) return 0