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 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 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 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 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 update(self): # Prevent updates while teleporting if self.is_teleporting: return now = time.time() if now > self.last_tick > 0: elapsed = now - self.last_tick # Update played time self.player.totaltime += elapsed self.player.leveltime += elapsed # Regeneration self.regenerate(now) # Attack update self.attack_update(elapsed) # Release spirit timer if not self.is_alive: if self.spirit_release_timer < 300: # 5 min self.spirit_release_timer += elapsed else: self.repop() self.last_tick = now if self.dirty: self.send_update_self() self.send_update_surrounding(self.generate_proper_update_packet()) GridManager.update_object(self) self.reset_fields() self.set_dirty(is_dirty=False, dirty_inventory=False)
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 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 handle(world_session, socket, reader): if len(reader.data ) >= 8: # Avoid handling empty quest giver status packet quest_giver_guid = unpack('<Q', reader.data[:8])[0] quest_giver = None # NPC if quest_giver_guid & HighGuid.HIGHGUID_UNIT: quest_giver = GridManager.get_surrounding_unit_by_guid( world_session.player_mgr, quest_giver_guid) # Gameobject elif quest_giver_guid & HighGuid.HIGHGUID_GAMEOBJECT: quest_giver = GridManager.get_surrounding_gameobject_by_guid( world_session.player_mgr, quest_giver_guid) if not quest_giver: return 0 quest_giver_status = QuestGiverStatus.QUEST_GIVER_NONE if world_session.player_mgr: if quest_giver.get_type() == ObjectTypes.TYPE_UNIT: quest_giver_status = world_session.player_mgr.quest_manager.get_dialog_status( quest_giver) elif quest_giver.get_type() == ObjectTypes.TYPE_GAMEOBJECT: # TODO: Proper handling for game object quest_giver_status = QuestGiverStatus.QUEST_GIVER_NONE else: Logger.error( f'Error in CMSG_QUESTGIVER_STATUS_QUERY, quest giver was an unexpected type of: {quest_giver.object_type}' ) world_session.player_mgr.quest_manager.send_quest_giver_status( quest_giver_guid, quest_giver_status) return 0
def handle(world_session, socket, reader): if len(reader.data) >= 8: # Avoid handling empty debug AI state packet guid = unpack('<Q', reader.data[:8])[0] world_object = GridManager.get_surrounding_unit_by_guid( world_session.player_mgr, guid, include_players=True) # If no Unit or Player, try to get a Gameobject. if not world_object: world_object = GridManager.get_surrounding_gameobject_by_guid( world_session.player_mgr, guid) # Still no object with that Guid? Return. if not world_object: return 0 messages = world_object.get_debug_messages() data = pack('<QI', guid, len(messages)) for message in messages: message_bytes = PacketWriter.string_to_bytes( message[:127]) # Max length is 128 (127 + null byte). data += pack(f'<{len(message_bytes)}s', message_bytes) world_session.enqueue_packet( PacketWriter.get_packet(OpCode.SMSG_DEBUG_AISTATE, data)) return 0
def send_chat_message(world_session, guid, chat_flags, message, chat_type, lang, range_): GridManager.send_surrounding_in_range(ChatManager._get_message_packet( guid, chat_flags, message, chat_type, lang), world_session.player_mgr, range_, use_ignore=True)
def handle(world_session, socket, reader): if world_session.player_mgr.is_alive and len(reader.data) >= 12: emote_text_id, guid = unpack('<IQ', reader.data) emote = DbcDatabaseManager.emote_text_get_by_id(emote_text_id) if emote: data = pack('<QI', world_session.player_mgr.guid, emote_text_id) target = GridManager.get_surrounding_unit_by_guid( world_session.player_mgr, guid, include_players=True) if not target: data += pack('<B', 0) elif target.get_type() == ObjectTypes.TYPE_PLAYER: player_name_bytes = PacketWriter.string_to_bytes( target.player.name) data += pack('<%us' % len(player_name_bytes), player_name_bytes) elif target.get_type( ) == ObjectTypes.TYPE_UNIT and target.creature_template: unit_name_bytes = PacketWriter.string_to_bytes( target.creature_template.name) data += pack('<%us' % len(unit_name_bytes), unit_name_bytes) else: data += pack('<B', 0) GridManager.send_surrounding_in_range( PacketWriter.get_packet(OpCode.SMSG_TEXT_EMOTE, data), world_session.player_mgr, config.World.Chat.ChatRange.emote_range) # Perform visual emote action if needed emote_id = emote.EmoteID state = StandState.UNIT_STANDING needs_broadcast = True if emote_text_id == Emotes.SIT: if not world_session.player_mgr.is_sitting: state = StandState.UNIT_SITTING world_session.player_mgr.stand_state = state elif emote_text_id == Emotes.STAND: world_session.player_mgr.stand_state = state elif emote_text_id == Emotes.SLEEP: if world_session.player_mgr.stand_state != StandState.UNIT_SLEEPING: state = StandState.UNIT_SLEEPING world_session.player_mgr.stand_state = state elif emote_text_id == Emotes.KNEEL: if world_session.player_mgr.stand_state != StandState.UNIT_KNEEL: state = StandState.UNIT_KNEEL world_session.player_mgr.stand_state = state else: needs_broadcast = False world_session.player_mgr.play_emote(emote_id) if needs_broadcast: world_session.player_mgr.flagged_for_update = True return 0
def handle_ack(world_session, socket, reader): world_session.player_mgr.send_update_self() world_session.player_mgr.send_update_surrounding() GridManager.update_object(world_session.player_mgr) world_session.player_mgr.is_teleporting = False return 0
def send_chat_message(world_session, guid, chat_flags, message, chat_type, lang, range_): GridManager.send_surrounding_in_range( ChatManager._get_message_packet(guid, chat_flags, message, chat_type, 0), # TODO Handle language world_session.player_mgr, range_)
def complete_login(self): self.is_online = True GridManager.update_object(self) self.send_update_surrounding( self.generate_proper_update_packet(create=True), include_self=False, create=True)
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 handle_ack(world_session, socket, reader): world_session.player_mgr.send_update_self(create=True) world_session.player_mgr.send_update_surrounding(world_session.player_mgr.generate_proper_update_packet( create=True), include_self=False, create=True) GridManager.update_object(world_session.player_mgr) world_session.player_mgr.is_teleporting = False 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) 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 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_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 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 logout(self): # TODO: Temp hackfix until groups are saved in db if self.group_manager: self.group_manager.leave_party( self, force_disband=self.group_manager.party_leader == self) self.session.save_character() GridManager.remove_object(self) self.session.player_mgr = None self.session = None self.is_online = False
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_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 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 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)