use tokio::net::TcpStream; use crate::{ common::{ObjectId, Position, timestamp_secs}, opcodes::ServerZoneIpcType, packet::{ CompressionType, ConnectionType, PacketSegment, PacketState, SegmentType, parse_packet, send_packet, }, }; use super::{ Actor, Event, Inventory, Item, LuaPlayer, Zone, ipc::{ ActorSetPos, ClientZoneIpcSegment, ContainerInfo, ContainerType, InitZone, ItemInfo, ServerZoneIpcData, ServerZoneIpcSegment, StatusEffect, StatusEffectList, UpdateClassInfo, WeatherChange, }, }; #[derive(Debug, Default, Clone, Copy)] pub struct PlayerData { // Static data pub actor_id: u32, pub content_id: u64, pub account_id: u32, pub classjob_id: u8, pub level: u8, pub curr_hp: u32, pub max_hp: u32, pub curr_mp: u16, pub max_mp: u16, // Dynamic data pub position: Position, /// In radians. pub rotation: f32, pub zone_id: u16, } #[derive(Debug, Default, Clone)] pub struct StatusEffects { pub status_effects: Vec, /// If the list is dirty and must be propagated to the client pub dirty: bool, } impl StatusEffects { pub fn add(&mut self, effect_id: u16, duration: f32) { let status_effect = self.find_or_create_status_effect(effect_id); status_effect.duration = duration; self.dirty = true } fn find_or_create_status_effect(&mut self, effect_id: u16) -> &mut StatusEffect { if let Some(i) = self .status_effects .iter() .position(|effect| effect.effect_id == effect_id) { &mut self.status_effects[i] } else { self.status_effects.push(StatusEffect { effect_id, ..Default::default() }); self.status_effects.last_mut().unwrap() } } } /// Represents a single connection between an instance of the client and the world server pub struct ZoneConnection { pub socket: TcpStream, pub state: PacketState, pub player_data: PlayerData, pub zone: Option, pub spawn_index: u8, pub inventory: Inventory, pub status_effects: StatusEffects, pub event: Option, pub actors: Vec, } impl ZoneConnection { pub async fn parse_packet( &mut self, data: &[u8], ) -> (Vec>, ConnectionType) { parse_packet(data, &mut self.state).await } pub async fn send_segment(&mut self, segment: PacketSegment) { send_packet( &mut self.socket, &mut self.state, ConnectionType::Zone, CompressionType::Oodle, &[segment], ) .await; } pub async fn set_player_position(&mut self, position: Position) { // set pos { let ipc = ServerZoneIpcSegment { op_code: ServerZoneIpcType::ActorSetPos, timestamp: timestamp_secs(), data: ServerZoneIpcData::ActorSetPos(ActorSetPos { unk: 0x020fa3b8, position, ..Default::default() }), ..Default::default() }; self.send_segment(PacketSegment { source_actor: self.player_data.actor_id, target_actor: self.player_data.actor_id, segment_type: SegmentType::Ipc { data: ipc }, }) .await; } } pub async fn change_zone(&mut self, new_zone_id: u16) { self.zone = Some(Zone::load(new_zone_id)); self.player_data.zone_id = new_zone_id; // Player Class Info { let ipc = ServerZoneIpcSegment { op_code: ServerZoneIpcType::UpdateClassInfo, timestamp: timestamp_secs(), data: ServerZoneIpcData::UpdateClassInfo(UpdateClassInfo { class_id: self.player_data.classjob_id as u16, unknown: 1, synced_level: self.player_data.level as u16, class_level: self.player_data.level as u16, ..Default::default() }), ..Default::default() }; self.send_segment(PacketSegment { source_actor: self.player_data.actor_id, target_actor: self.player_data.actor_id, segment_type: SegmentType::Ipc { data: ipc }, }) .await; } // link shell information { let ipc = ServerZoneIpcSegment { op_code: ServerZoneIpcType::LinkShellInformation, timestamp: timestamp_secs(), data: ServerZoneIpcData::LinkShellInformation { unk: [0; 456] }, ..Default::default() }; self.send_segment(PacketSegment { source_actor: self.player_data.actor_id, target_actor: self.player_data.actor_id, segment_type: SegmentType::Ipc { data: ipc }, }) .await; } // TODO: send unk16? // Init Zone { let ipc = ServerZoneIpcSegment { op_code: ServerZoneIpcType::InitZone, timestamp: timestamp_secs(), data: ServerZoneIpcData::InitZone(InitZone { server_id: 0, zone_id: self.zone.as_ref().unwrap().id, weather_id: 1, ..Default::default() }), ..Default::default() }; self.send_segment(PacketSegment { source_actor: self.player_data.actor_id, target_actor: self.player_data.actor_id, segment_type: SegmentType::Ipc { data: ipc }, }) .await; } } pub async fn change_weather(&mut self, new_weather_id: u16) { let ipc = ServerZoneIpcSegment { op_code: ServerZoneIpcType::WeatherChange, timestamp: timestamp_secs(), data: ServerZoneIpcData::WeatherChange(WeatherChange { weather_id: new_weather_id, transistion_time: 1.0, }), ..Default::default() }; self.send_segment(PacketSegment { source_actor: self.player_data.actor_id, target_actor: self.player_data.actor_id, segment_type: SegmentType::Ipc { data: ipc }, }) .await; } pub fn get_free_spawn_index(&mut self) -> u8 { self.spawn_index += 1; self.spawn_index } pub async fn send_inventory(&mut self) { // item list { let equipped = self.inventory.equipped; let mut send_slot = async |slot_index: u16, item: &Item| { let ipc = ServerZoneIpcSegment { op_code: ServerZoneIpcType::ItemInfo, timestamp: timestamp_secs(), data: ServerZoneIpcData::ItemInfo(ItemInfo { container: ContainerType::Equipped, slot: slot_index, quantity: item.quantity, catalog_id: item.id, condition: 30000, ..Default::default() }), ..Default::default() }; self.send_segment(PacketSegment { source_actor: self.player_data.actor_id, target_actor: self.player_data.actor_id, segment_type: SegmentType::Ipc { data: ipc }, }) .await; }; send_slot(0, &equipped.main_hand).await; send_slot(1, &equipped.off_hand).await; send_slot(2, &equipped.head).await; send_slot(3, &equipped.body).await; send_slot(4, &equipped.hands).await; send_slot(6, &equipped.legs).await; send_slot(7, &equipped.feet).await; send_slot(8, &equipped.ears).await; send_slot(9, &equipped.neck).await; send_slot(10, &equipped.wrists).await; send_slot(11, &equipped.right_ring).await; send_slot(12, &equipped.left_ring).await; send_slot(13, &equipped.soul_crystal).await; } // inform the client they have items equipped { let ipc = ServerZoneIpcSegment { op_code: ServerZoneIpcType::ContainerInfo, timestamp: timestamp_secs(), data: ServerZoneIpcData::ContainerInfo(ContainerInfo { container: ContainerType::Equipped, num_items: self.inventory.equipped.num_items(), ..Default::default() }), ..Default::default() }; self.send_segment(PacketSegment { source_actor: self.player_data.actor_id, target_actor: self.player_data.actor_id, segment_type: SegmentType::Ipc { data: ipc }, }) .await; } } pub async fn send_message(&mut self, message: &str) { let ipc = ServerZoneIpcSegment { op_code: ServerZoneIpcType::ServerChatMessage, timestamp: timestamp_secs(), data: ServerZoneIpcData::ServerChatMessage { message: message.to_string(), unk: 0, }, ..Default::default() }; self.send_segment(PacketSegment { source_actor: self.player_data.actor_id, target_actor: self.player_data.actor_id, segment_type: SegmentType::Ipc { data: ipc }, }) .await; } pub async fn process_lua_player(&mut self, player: &mut LuaPlayer) { for segment in &player.queued_segments { self.send_segment(segment.clone()).await; } player.queued_segments.clear(); for task in &player.queued_tasks { self.change_zone(task.zone_id).await; } player.queued_tasks.clear(); } pub async fn process_effects_list(&mut self) { // Only update the client if absolutely nessecary (e.g. an effect is added, removed or changed duration) if self.status_effects.dirty { let mut list = [StatusEffect::default(); 30]; list[..self.status_effects.status_effects.len()] .copy_from_slice(&self.status_effects.status_effects); let ipc = ServerZoneIpcSegment { op_code: ServerZoneIpcType::StatusEffectList, timestamp: timestamp_secs(), data: ServerZoneIpcData::StatusEffectList(StatusEffectList { statues: list, classjob_id: self.player_data.classjob_id, level: self.player_data.level, curr_hp: self.player_data.curr_hp, max_hp: self.player_data.max_hp, curr_mp: self.player_data.curr_mp, max_mp: self.player_data.max_mp, ..Default::default() }), ..Default::default() }; self.send_segment(PacketSegment { source_actor: self.player_data.actor_id, target_actor: self.player_data.actor_id, segment_type: SegmentType::Ipc { data: ipc }, }) .await; self.status_effects.dirty = false; } } pub async fn update_hp_mp(&mut self, actor_id: ObjectId, hp: u32, mp: u16) { let ipc = ServerZoneIpcSegment { op_code: ServerZoneIpcType::UpdateHpMpTp, timestamp: timestamp_secs(), data: ServerZoneIpcData::UpdateHpMpTp { hp, mp, unk: 0 }, ..Default::default() }; self.send_segment(PacketSegment { source_actor: actor_id.0, target_actor: self.player_data.actor_id, segment_type: SegmentType::Ipc { data: ipc }, }) .await; } pub fn add_actor(&mut self, actor: Actor) { self.actors.push(actor); } pub fn get_actor(&mut self, id: ObjectId) -> Option<&mut Actor> { for actor in &mut self.actors { if actor.id == id { return Some(actor); } } return None; } }